Skip to content

WIP: feat(observability): collect per-activity resource usage for tier sizing - #3282

Draft
OnkarVO7 wants to merge 11 commits into
mainfrom
onkarravgan/sizing-telemetry
Draft

OnkarVO7 wants to merge 11 commits into
mainfrom
onkarravgan/sizing-telemetry

Conversation

@OnkarVO7

Copy link
Copy Markdown
Collaborator

Changelog

Collects per-activity resource usage so worker tier envelopes can be re-derived from measured data instead of estimated. Collection only — nothing here reads or decides a tier. Ships off.

  • application_sdk/observability/cgroup.py (new) — container-level readers: memory.current / memory.max / memory.peak and cpu.stat (usage_usec, nr_periods, nr_throttled, throttled_usec), cgroup v2 with v1 fallbacks, plus track_container_usage() as an async context manager.
  • application_sdk/observability/sizing.py (new) — SizingObservation record, four OTel histograms (activity.sizing.peak_memory_mib / peak_memory_fraction / cpu_throttled_fraction / mean_cpu_cores), and one structured activity_sizing_observation log line per execution.
  • interceptors/sizing.py (new) — SizingTelemetryInterceptor, wired by create_worker, gated on an allow-list of activity names.
  • execution/settings.pyAPPLICATION_SDK_ENABLE_SIZING_TELEMETRY (default false), APPLICATION_SDK_SIZING_TELEMETRY_ACTIVITIES (default empty), APPLICATION_SDK_SIZING_TELEMETRY_POLL_SECONDS (default 1.0).

Why not extend resource_sampler

It reads the process, which is the right instrument for App Vitals and the wrong one for sizing:

  1. RSS is not what the OOM killer acts on — the kernel kills on the cgroup's memory.current, which includes page cache and any child process. An activity reading a large Parquet file through the page cache is under-measured, in the direction that makes a too-small tier look safe.
  2. Start/end point samples miss the peak — a tier has to cover the maximum, not the value at the moment the activity finished. A query that builds a 12 GiB hash table and releases it reads small at both ends.
  3. CPU seconds cannot distinguish cheap from starved — an activity given a 1-core quota and needing 3 reports ~1 core-second per wall second and looks perfectly sized. cpu.stat's throttling counters are the only signal that separates the two.

Design notes for review

Peak memory takes the cheapest instrument that works. memory.peak is reset on entry and read on exit (two file reads per activity, catches spikes of any duration); the background poller is used only when that reset cannot be proven. Proof rather than kernel-version sniffing — memory.peak is only writable from Linux 6.8, a write can succeed as a no-op, and the v1/v2 split makes any version table unreliable — so it reads the watermark back and requires it to have dropped. An unproven reset would report the pod's lifetime watermark as this activity's peak. peak_source is recorded on every observation because a watermark peak and a polled peak have different blind spots.

A 1-second poll default is affordable because there is no RPC. Deliberately unlike AE's report_memory_pressure, whose per-tick activity.heartbeat() is a network call — that one is a safety device whose readings must reach the workflow; this one only has to reach the local process.

An interceptor, not a decorator. A decorator has to be applied by every app author to every task method, and the teams that forget are exactly the ones with no sizing data — so the dataset would skew towards teams who already care about resource usage. create_worker already attaches SDK interceptors to every activity in every v3 app.

A sibling of MetricsInterceptor, not an extension. This one is gated and that one is unconditional; the App Vitals path should not gain a background task and a set of cgroup reads as a side effect of a sizing rollout. Not exported from the interceptors package and not accepted via create_worker(interceptors=...), which makes double-registration — and therefore two pollers per activity — impossible by construction rather than by a guard list.

Empty allow-list collects nothing. Sizing data is only worth collecting for activities whose resource use varies with the data they process; most activities are fixed-cost bookkeeping, and measuring them adds rows without adding information. Empty is also the fail-closed direction, so a tenant that sets the enable flag and forgets the list gets no telemetry rather than telemetry on everything. Since that is silent, the worker warns at startup. "*" selects everything, for a discovery pass on a test tenant.

The allow-list matches the bare task name as well as the qualified one. A v3 activity registers as "{app_name}:{task_name}", so activity_type is "automation-engine:merge" — but an author reading @task async def merge will write merge. Matching only the qualified form silently collected nothing: config looks right, worker logs the activities it is measuring, dataset comes back empty. A qualified entry still narrows to one app.

Nulls stay distinguishable from zeros. Every reader returns None rather than raising or guessing, and an observation with nothing measured is dropped rather than emitted — a null read downstream as a zero would fit the smallest tier to an activity nobody measured.

Telemetry never fails the activity it measures. The setup block, the poller-cancel path and the finalisation are each guarded; tests pin all three, including that the block's own exception still propagates.

Additional context

  • Companion AE change: atlanhq/atlan-automation-engine-app#1089 (6cf5f0bc). AE builds its own worker and so owns its own interceptor list, meaning SDK interceptors reach the whole fleet except AE — it needs the interceptor attached by hand. That change is inert until this ships, since AE pins >=3.15.1 and 3.15.1 has neither the interceptor nor the settings fields.
  • This is the "collect data" stage of collect → classify tiers → productionise. Still to come: an input_bytes driver variable, a durable columnar sink (the structured log line is the only sink today), cross-tenant curation, and the analysis that emits the tier table.

Checklist

  • Additional tests added — 70 new tests (34 cgroup, 32 interceptor/record/settings, 4 worker wiring); 7290 unit tests pass; pre-commit incl. pyright clean
  • All CI checks passed
  • Relevant documentation updated

Warning

Not verified on a real cgroup. Everything is tested against fake hierarchies on disk (macOS has no cgroup). Which peak mode actually engages on a tenant — watermark or poll — depends on the node kernel, and memory.peak is only resettable from 6.8, so poll is the likely path on current GKE/EKS nodes. peak_source is on every observation so this is checkable the moment collection is switched on. This is the main reason the PR is a draft.

Note

Conformance. 9 findings remain in the new files (broad-except guards, contextlib.suppress, loop-swallowed OSError) — the same intentional-and-documented class as the existing observability interceptors, which report the same codes despite carrying # conformance: ignore[...] comments (those are advisory, not machine-enforced). Repo total went 523 → 519, because conformance caught four real issues in this code: an assign-only except, jsonorjson, and a missing exc_info=True.


Copyleft License Compliance

  • Have you used any code that is subject to a Copyleft license (e.g., GPL, AGPL, LGPL)?
  • If yes, have you modified the code in the context of this project? please share additional details.

🤖 Generated with Claude Code

OnkarVO7 and others added 4 commits August 19, 2026 17:33
Adds application_sdk.observability.cgroup alongside resource_sampler. The
existing sampler reads the process (/proc/self/stat RSS, getrusage CPU), which
is right for App Vitals and wrong for sizing decisions:

- RSS is not what the OOM killer acts on; memory.current is.
- Start/end point samples miss the peak a tier has to cover.
- CPU seconds cannot tell a cheap activity from a throttled one.

Peak memory takes the cheapest instrument that works: the kernel's memory.peak
watermark is reset on entry and read on exit (two file reads per activity), and
a background poller is used only when that reset cannot be *proven* to have
taken effect. Proof rather than kernel-version sniffing, because memory.peak is
only writable from Linux 6.8 and a write can succeed as a no-op.

Every reader returns None rather than raising or guessing, and None stays
distinguishable from 0 — sizing on a silent 0 would pick the smallest tier for
every activity.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eptor

Adds SizingTelemetryInterceptor, wired by create_worker and gated on
APPLICATION_SDK_ENABLE_SIZING_TELEMETRY (default off), so a version bump alone
changes nothing on any tenant.

An interceptor rather than a decorator: a decorator has to be applied by every
app author to every task method, and the teams that forget are exactly the ones
with no sizing data — the dataset would be biased towards teams who already
care about resource usage. create_worker already attaches SDK interceptors to
every activity in every v3 app, so this is the only hook that is uniform by
construction, and it needs nothing from the activity signature.

A sibling of MetricsInterceptor, not an extension: this one is gated and that
one is unconditional, and the App Vitals metrics path should not gain a
background task and a set of cgroup reads as a side effect of a sizing rollout.

Emits four OTel histograms (peak memory MiB and fraction, CPU throttled
fraction, mean cores) plus one structured activity_sizing_observation log line
per execution for offline tier fitting. Labels are bounded — no workflow_id.
Nothing here reads or decides a tier: measurement that depended on routing
would make the calibration circular.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds APPLICATION_SDK_SIZING_TELEMETRY_ACTIVITIES, a comma-separated list of
activity names to measure. Empty collects nothing.

Sizing data is only worth collecting for activities whose resource use varies
with the data they process. Most activities are fixed-cost bookkeeping, and
measuring them adds rows to the dataset the tier table is fitted from without
adding information — so this is opt-in by name, not a default-on sweep.

Empty is the fail-closed direction: a tenant that sets the enable flag and
forgets the list gets no telemetry rather than telemetry on everything. Since
that is silent, the worker warns at startup when it happens. '*' selects every
activity, for a discovery pass on a test tenant.

The filter runs before the tracker is constructed, so an unselected activity
costs a set lookup — no cgroup reads, no poller. A test asserts on the tracker
rather than on the record, because filtering later would also record nothing
while still paying setup on every activity in the app.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A v3 activity registers with Temporal as "{app_name}:{task_name}", so
activity.info().activity_type is "automation-engine:merge" — but an app author
reading their own source sees '@task async def merge' and will write "merge".

Matching only the qualified form meant the obvious spelling silently collected
nothing: the config looks right, the worker logs the activities it is measuring,
and the dataset comes back empty. Both forms now match, and a qualified entry
still narrows to one app rather than matching the same task name elsewhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

📜 Docstring Coverage Report

RESULT: PASSED (minimum: 30.0%, actual: 79.0%)

Detailed Coverage Report
======= Coverage for /home/runner/work/application-sdk/application-sdk/ ========
----------------------------------- Summary ------------------------------------
| Name                                                                                         | Total | Miss | Cover | Cover% |
|----------------------------------------------------------------------------------------------|-------|------|-------|--------|
| .claude/skills/capability-manifest/references/extractor.py                                   |    31 |    2 |    29 |    94% |
| application_sdk/__init__.py                                                                  |     1 |    0 |     1 |   100% |
| application_sdk/_discovery_errors.py                                                         |     7 |    0 |     7 |   100% |
| application_sdk/constants.py                                                                 |     5 |    2 |     3 |    60% |
| application_sdk/discovery.py                                                                 |    12 |    3 |     9 |    75% |
| application_sdk/main.py                                                                      |    38 |    8 |    30 |    79% |
| application_sdk/main_errors.py                                                               |     5 |    0 |     5 |   100% |
| application_sdk/version.py                                                                   |     1 |    0 |     1 |   100% |
| application_sdk/_runtime/__init__.py                                                         |     1 |    0 |     1 |   100% |
| application_sdk/_runtime/offload.py                                                          |    11 |    1 |    10 |    91% |
| application_sdk/_runtime/progress.py                                                         |    32 |    2 |    30 |    94% |
| application_sdk/app/__init__.py                                                              |     1 |    0 |     1 |   100% |
| application_sdk/app/_ep_registration.py                                                      |     6 |    0 |     6 |   100% |
| application_sdk/app/base.py                                                                  |    81 |   19 |    62 |    77% |
| application_sdk/app/base_errors.py                                                           |     6 |    0 |     6 |   100% |
| application_sdk/app/client.py                                                                |     1 |    0 |     1 |   100% |
| application_sdk/app/context.py                                                               |    40 |    2 |    38 |    95% |
| application_sdk/app/entrypoint.py                                                            |    15 |    4 |    11 |    73% |
| application_sdk/app/registry.py                                                              |    40 |   11 |    29 |    72% |
| application_sdk/app/task.py                                                                  |    17 |    6 |    11 |    65% |
| application_sdk/clients/__init__.py                                                          |     2 |    1 |     1 |    50% |
| application_sdk/clients/_interface.py                                                        |     4 |    1 |     3 |    75% |
| application_sdk/clients/base.py                                                              |     6 |    1 |     5 |    83% |
| application_sdk/clients/models.py                                                            |     2 |    0 |     2 |   100% |
| application_sdk/clients/redis.py                                                             |    27 |    0 |    27 |   100% |
| application_sdk/clients/redis_errors.py                                                      |     5 |    0 |     5 |   100% |
| application_sdk/clients/sql.py                                                               |    23 |    1 |    22 |    96% |
| application_sdk/clients/sql_errors.py                                                        |    11 |    0 |    11 |   100% |
| application_sdk/clients/sql_typecasters.py                                                   |    14 |    4 |    10 |    71% |
| application_sdk/clients/ssl_utils.py                                                         |     8 |    0 |     8 |   100% |
| application_sdk/clients/azure/__init__.py                                                    |     1 |    0 |     1 |   100% |
| application_sdk/clients/azure/auth.py                                                        |     7 |    0 |     7 |   100% |
| application_sdk/clients/azure/azure_errors.py                                                |     8 |    0 |     8 |   100% |
| application_sdk/clients/azure/client.py                                                      |    13 |    0 |    13 |   100% |
| application_sdk/common/__init__.py                                                           |     1 |    0 |     1 |   100% |
| application_sdk/common/_env.py                                                               |     2 |    0 |     2 |   100% |
| application_sdk/common/_listing.py                                                           |     6 |    0 |     6 |   100% |
| application_sdk/common/atomic.py                                                             |    13 |    0 |    13 |   100% |
| application_sdk/common/aws_utils.py                                                          |    10 |    1 |     9 |    90% |
| application_sdk/common/aws_utils_errors.py                                                   |     7 |    0 |     7 |   100% |
| application_sdk/common/concurrency.py                                                        |     3 |    0 |     3 |   100% |
| application_sdk/common/env_warnings.py                                                       |     2 |    0 |     2 |   100% |
| application_sdk/common/error_codes.py                                                        |    15 |    3 |    12 |    80% |
| application_sdk/common/errors.py                                                             |     7 |    0 |     7 |   100% |
| application_sdk/common/file_converter.py                                                     |     9 |    5 |     4 |    44% |
| application_sdk/common/file_ops.py                                                           |    16 |    1 |    15 |    94% |
| application_sdk/common/filter_matching.py                                                    |     9 |    3 |     6 |    67% |
| application_sdk/common/models.py                                                             |     4 |    2 |     2 |    50% |
| application_sdk/common/path.py                                                               |     2 |    1 |     1 |    50% |
| application_sdk/common/spillable_dict.py                                                     |    17 |   11 |     6 |    35% |
| application_sdk/common/sql_filters.py                                                        |    14 |    2 |    12 |    86% |
| application_sdk/common/sql_filters_errors.py                                                 |     2 |    0 |     2 |   100% |
| application_sdk/common/task_queue.py                                                         |    10 |    0 |    10 |   100% |
| application_sdk/common/transforms.py                                                         |     5 |    0 |     5 |   100% |
| application_sdk/common/types.py                                                              |     2 |    0 |     2 |   100% |
| application_sdk/common/utils.py                                                              |     2 |    0 |     2 |   100% |
| application_sdk/common/incremental/__init__.py                                               |     1 |    1 |     0 |     0% |
| application_sdk/common/incremental/helpers.py                                                |    11 |    0 |    11 |   100% |
| application_sdk/common/incremental/incremental_errors.py                                     |    11 |    0 |    11 |   100% |
| application_sdk/common/incremental/marker.py                                                 |     5 |    0 |     5 |   100% |
| application_sdk/common/incremental/models.py                                                 |    10 |    0 |    10 |   100% |
| application_sdk/common/incremental/column_extraction/__init__.py                             |     1 |    0 |     1 |   100% |
| application_sdk/common/incremental/column_extraction/analysis.py                             |     3 |    0 |     3 |   100% |
| application_sdk/common/incremental/column_extraction/backfill.py                             |     3 |    0 |     3 |   100% |
| application_sdk/common/incremental/state/__init__.py                                         |     1 |    1 |     0 |     0% |
| application_sdk/common/incremental/state/incremental_diff.py                                 |     8 |    0 |     8 |   100% |
| application_sdk/common/incremental/state/state_reader.py                                     |     2 |    0 |     2 |   100% |
| application_sdk/common/incremental/state/state_writer.py                                     |    10 |    0 |    10 |   100% |
| application_sdk/common/incremental/state/table_scope.py                                      |     8 |    0 |     8 |   100% |
| application_sdk/common/incremental/storage/__init__.py                                       |     1 |    1 |     0 |     0% |
| application_sdk/common/incremental/storage/duckdb_utils.py                                   |    12 |    2 |    10 |    83% |
| application_sdk/common/incremental/storage/rocksdb_utils.py                                  |     3 |    0 |     3 |   100% |
| application_sdk/contracts/__init__.py                                                        |     1 |    0 |     1 |   100% |
| application_sdk/contracts/base.py                                                            |    37 |    7 |    30 |    81% |
| application_sdk/contracts/cleanup.py                                                         |     5 |    0 |     5 |   100% |
| application_sdk/contracts/compat.py                                                          |     9 |    1 |     8 |    89% |
| application_sdk/contracts/events.py                                                          |    12 |    0 |    12 |   100% |
| application_sdk/contracts/storage.py                                                         |     6 |    1 |     5 |    83% |
| application_sdk/contracts/types.py                                                           |    15 |    0 |    15 |   100% |
| application_sdk/contracts/types_errors.py                                                    |     2 |    0 |     2 |   100% |
| application_sdk/credentials/__init__.py                                                      |     1 |    0 |     1 |   100% |
| application_sdk/credentials/agent.py                                                         |    13 |    3 |    10 |    77% |
| application_sdk/credentials/atlan.py                                                         |    12 |    6 |     6 |    50% |
| application_sdk/credentials/atlan_client.py                                                  |     6 |    0 |     6 |   100% |
| application_sdk/credentials/errors.py                                                        |    20 |   12 |     8 |    40% |
| application_sdk/credentials/git.py                                                           |     9 |    6 |     3 |    33% |
| application_sdk/credentials/ingress.py                                                       |     8 |    0 |     8 |   100% |
| application_sdk/credentials/oauth.py                                                         |    13 |    2 |    11 |    85% |
| application_sdk/credentials/ref.py                                                           |    17 |    1 |    16 |    94% |
| application_sdk/credentials/registry.py                                                      |    11 |    3 |     8 |    73% |
| application_sdk/credentials/resolver.py                                                      |    11 |    4 |     7 |    64% |
| application_sdk/credentials/spec.py                                                          |     6 |    1 |     5 |    83% |
| application_sdk/credentials/types.py                                                         |    35 |   17 |    18 |    51% |
| application_sdk/credentials/utils.py                                                         |     4 |    1 |     3 |    75% |
| application_sdk/dev/__init__.py                                                              |     1 |    0 |     1 |   100% |
| application_sdk/dev/_dapr.py                                                                 |    11 |    2 |     9 |    82% |
| application_sdk/dev/_dapr_errors.py                                                          |     7 |    6 |     1 |    14% |
| application_sdk/dev/_embedded.py                                                             |     3 |    0 |     3 |   100% |
| application_sdk/errors/__init__.py                                                           |     4 |    1 |     3 |    75% |
| application_sdk/errors/base.py                                                               |    10 |    2 |     8 |    80% |
| application_sdk/errors/categories.py                                                         |     3 |    0 |     3 |   100% |
| application_sdk/errors/leaves.py                                                             |    22 |    8 |    14 |    64% |
| application_sdk/errors/wire.py                                                               |     4 |    1 |     3 |    75% |
| application_sdk/execution/__init__.py                                                        |     1 |    0 |     1 |   100% |
| application_sdk/execution/decorators.py                                                      |     3 |    2 |     1 |    33% |
| application_sdk/execution/errors.py                                                          |     2 |    0 |     2 |   100% |
| application_sdk/execution/heartbeat.py                                                       |    17 |    2 |    15 |    88% |
| application_sdk/execution/progress.py                                                        |     6 |    0 |     6 |   100% |
| application_sdk/execution/progress_telemetry.py                                              |     7 |    1 |     6 |    86% |
| application_sdk/execution/retry.py                                                           |     9 |    0 |     9 |   100% |
| application_sdk/execution/run_length.py                                                      |     7 |    1 |     6 |    86% |
| application_sdk/execution/sandbox.py                                                         |     4 |    0 |     4 |   100% |
| application_sdk/execution/settings.py                                                        |     9 |    2 |     7 |    78% |
| application_sdk/execution/shutdown.py                                                        |     4 |    0 |     4 |   100% |
| application_sdk/execution/_temporal/__init__.py                                              |     1 |    1 |     0 |     0% |
| application_sdk/execution/_temporal/_activity_errors.py                                      |     8 |    0 |     8 |   100% |
| application_sdk/execution/_temporal/_backend_errors.py                                       |     5 |    4 |     1 |    20% |
| application_sdk/execution/_temporal/_lock_errors.py                                          |     5 |    0 |     5 |   100% |
| application_sdk/execution/_temporal/activities.py                                            |    11 |    0 |    11 |   100% |
| application_sdk/execution/_temporal/activity_utils.py                                        |     6 |    0 |     6 |   100% |
| application_sdk/execution/_temporal/auth.py                                                  |    13 |    0 |    13 |   100% |
| application_sdk/execution/_temporal/backend.py                                               |    15 |    1 |    14 |    93% |
| application_sdk/execution/_temporal/converter.py                                             |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/eviction_retry.py                                        |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/lock_activities.py                                       |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/preflight_gate.py                                        |    33 |    4 |    29 |    88% |
| application_sdk/execution/_temporal/sdr.py                                                   |    16 |    7 |     9 |    56% |
| application_sdk/execution/_temporal/worker.py                                                |    15 |    6 |     9 |    60% |
| application_sdk/execution/_temporal/workflows.py                                             |     2 |    0 |     2 |   100% |
| application_sdk/execution/_temporal/interceptors/__init__.py                                 |     1 |    0 |     1 |   100% |
| application_sdk/execution/_temporal/interceptors/events.py                                   |    13 |    0 |    13 |   100% |
| application_sdk/execution/_temporal/interceptors/liveness.py                                 |    11 |    9 |     2 |    18% |
| application_sdk/execution/_temporal/interceptors/lock.py                                     |    10 |    2 |     8 |    80% |
| application_sdk/execution/_temporal/interceptors/log.py                                      |    22 |   12 |    10 |    45% |
| application_sdk/execution/_temporal/interceptors/metrics.py                                  |    18 |   15 |     3 |    17% |
| application_sdk/execution/_temporal/interceptors/outputs.py                                  |     9 |    0 |     9 |   100% |
| application_sdk/execution/_temporal/interceptors/sizing.py                                   |    10 |    6 |     4 |    40% |
| application_sdk/execution/_temporal/interceptors/trace.py                                    |     6 |    4 |     2 |    33% |
| application_sdk/handler/__init__.py                                                          |     1 |    0 |     1 |   100% |
| application_sdk/handler/base.py                                                              |    14 |    3 |    11 |    79% |
| application_sdk/handler/context.py                                                           |    18 |    5 |    13 |    72% |
| application_sdk/handler/contracts.py                                                         |    38 |    5 |    33 |    87% |
| application_sdk/handler/manifest.py                                                          |     5 |    0 |     5 |   100% |
| application_sdk/handler/service.py                                                           |    65 |   23 |    42 |    65% |
| application_sdk/handler/service_errors.py                                                    |     4 |    0 |     4 |   100% |
| application_sdk/infrastructure/__init__.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_secret_utils.py                                              |     2 |    0 |     2 |   100% |
| application_sdk/infrastructure/bindings.py                                                   |    16 |    3 |    13 |    81% |
| application_sdk/infrastructure/capacity.py                                                   |    11 |    0 |    11 |   100% |
| application_sdk/infrastructure/context.py                                                    |     6 |    0 |     6 |   100% |
| application_sdk/infrastructure/credential_vault.py                                           |     7 |    3 |     4 |    57% |
| application_sdk/infrastructure/pubsub.py                                                     |    13 |    3 |    10 |    77% |
| application_sdk/infrastructure/secrets.py                                                    |    25 |    8 |    17 |    68% |
| application_sdk/infrastructure/state.py                                                      |    10 |    7 |     3 |    30% |
| application_sdk/infrastructure/_dapr/__init__.py                                             |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_dapr/_dapr_errors.py                                         |     3 |    0 |     3 |   100% |
| application_sdk/infrastructure/_dapr/client.py                                               |    31 |    4 |    27 |    87% |
| application_sdk/infrastructure/_dapr/credential_vault.py                                     |    18 |    7 |    11 |    61% |
| application_sdk/infrastructure/_dapr/http.py                                                 |    22 |   14 |     8 |    36% |
| application_sdk/infrastructure/_redis/__init__.py                                            |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_redis/capacity.py                                            |     9 |    4 |     5 |    56% |
| application_sdk/observability/__init__.py                                                    |     1 |    1 |     0 |     0% |
| application_sdk/observability/_objectstore_metric_exporter.py                                |    14 |    8 |     6 |    43% |
| application_sdk/observability/_objectstore_metric_reader.py                                  |     2 |    0 |     2 |   100% |
| application_sdk/observability/_prometheus_enrichment.py                                      |     6 |    3 |     3 |    50% |
| application_sdk/observability/cgroup.py                                                      |    18 |    1 |    17 |    94% |
| application_sdk/observability/context.py                                                     |     6 |    0 |     6 |   100% |
| application_sdk/observability/correlation.py                                                 |     6 |    0 |     6 |   100% |
| application_sdk/observability/dapr_log_forwarder.py                                          |    14 |    4 |    10 |    71% |
| application_sdk/observability/logger_adaptor.py                                              |    55 |    9 |    46 |    84% |
| application_sdk/observability/logger_adaptor_errors.py                                       |     2 |    0 |     2 |   100% |
| application_sdk/observability/metrics.py                                                     |     8 |    6 |     2 |    25% |
| application_sdk/observability/metrics_adaptor.py                                             |    13 |    2 |    11 |    85% |
| application_sdk/observability/models.py                                                      |     6 |    0 |     6 |   100% |
| application_sdk/observability/observability.py                                               |    22 |    4 |    18 |    82% |
| application_sdk/observability/pushgateway.py                                                 |    16 |   11 |     5 |    31% |
| application_sdk/observability/pushgateway_errors.py                                          |     3 |    0 |     3 |   100% |
| application_sdk/observability/resource_sampler.py                                            |     6 |    0 |     6 |   100% |
| application_sdk/observability/segment_client.py                                              |    15 |    1 |    14 |    93% |
| application_sdk/observability/sizing.py                                                      |    14 |    7 |     7 |    50% |
| application_sdk/observability/sizing_census.py                                               |     8 |    3 |     5 |    62% |
| application_sdk/observability/sizing_inputs.py                                               |    13 |    3 |    10 |    77% |
| application_sdk/observability/sizing_sink.py                                                 |    13 |    2 |    11 |    85% |
| application_sdk/observability/trace_context.py                                               |     2 |    0 |     2 |   100% |
| application_sdk/observability/traces_adaptor.py                                              |    15 |    1 |    14 |    93% |
| application_sdk/observability/utils.py                                                       |     7 |    1 |     6 |    86% |
| application_sdk/observability/decorators/observability_decorator.py                          |     7 |    4 |     3 |    43% |
| application_sdk/outputs/__init__.py                                                          |     2 |    0 |     2 |   100% |
| application_sdk/outputs/collector.py                                                         |     9 |    0 |     9 |   100% |
| application_sdk/outputs/models.py                                                            |     3 |    0 |     3 |   100% |
| application_sdk/server/__init__.py                                                           |     1 |    0 |     1 |   100% |
| application_sdk/server/health.py                                                             |    24 |    0 |    24 |   100% |
| application_sdk/server/fastapi/models.py                                                     |    21 |   17 |     4 |    19% |
| application_sdk/server/fastapi/utils.py                                                      |     5 |    0 |     5 |   100% |
| application_sdk/server/mcp/__init__.py                                                       |     2 |    2 |     0 |     0% |
| application_sdk/server/mcp/decorators.py                                                     |     3 |    1 |     2 |    67% |
| application_sdk/server/mcp/models.py                                                         |     2 |    2 |     0 |     0% |
| application_sdk/server/mcp/server.py                                                         |     7 |    1 |     6 |    86% |
| application_sdk/server/middleware/__init__.py                                                |     1 |    0 |     1 |   100% |
| application_sdk/server/middleware/_constants.py                                              |     1 |    0 |     1 |   100% |
| application_sdk/server/middleware/log.py                                                     |     4 |    3 |     1 |    25% |
| application_sdk/storage/__init__.py                                                          |     1 |    0 |     1 |   100% |
| application_sdk/storage/_concurrency.py                                                      |     3 |    1 |     2 |    67% |
| application_sdk/storage/_credential_providers.py                                             |     6 |    0 |     6 |   100% |
| application_sdk/storage/_obstore_config.py                                                   |    13 |    0 |    13 |   100% |
| application_sdk/storage/_telemetry.py                                                        |     5 |    0 |     5 |   100% |
| application_sdk/storage/batch.py                                                             |    17 |    4 |    13 |    76% |
| application_sdk/storage/binding.py                                                           |    27 |    1 |    26 |    96% |
| application_sdk/storage/chunked.py                                                           |    10 |    0 |    10 |   100% |
| application_sdk/storage/cloud.py                                                             |    24 |    6 |    18 |    75% |
| application_sdk/storage/errors.py                                                            |    36 |   24 |    12 |    33% |
| application_sdk/storage/factory.py                                                           |     3 |    0 |     3 |   100% |
| application_sdk/storage/file_ref_sync.py                                                     |    13 |    3 |    10 |    77% |
| application_sdk/storage/integrity.py                                                         |    14 |    0 |    14 |   100% |
| application_sdk/storage/ops.py                                                               |    29 |    1 |    28 |    97% |
| application_sdk/storage/preflight.py                                                         |     9 |    0 |     9 |   100% |
| application_sdk/storage/reference.py                                                         |     9 |    1 |     8 |    89% |
| application_sdk/storage/rolling.py                                                           |    33 |   12 |    21 |    64% |
| application_sdk/storage/rolling_errors.py                                                    |     4 |    0 |     4 |   100% |
| application_sdk/storage/transfer.py                                                          |    15 |    3 |    12 |    80% |
| application_sdk/storage/formats/__init__.py                                                  |    32 |    0 |    32 |   100% |
| application_sdk/storage/formats/format_errors.py                                             |    16 |    0 |    16 |   100% |
| application_sdk/storage/formats/json.py                                                      |    16 |    6 |    10 |    62% |
| application_sdk/storage/formats/parquet.py                                                   |    33 |    5 |    28 |    85% |
| application_sdk/storage/formats/utils.py                                                     |    10 |    2 |     8 |    80% |
| application_sdk/templates/__init__.py                                                        |     2 |    1 |     1 |    50% |
| application_sdk/templates/_template_errors.py                                                |    10 |    0 |    10 |   100% |
| application_sdk/templates/base_metadata_extractor.py                                         |     4 |    1 |     3 |    75% |
| application_sdk/templates/incremental_sql_metadata_extractor.py                              |    19 |    2 |    17 |    89% |
| application_sdk/templates/sql_app.py                                                         |    47 |    6 |    41 |    87% |
| application_sdk/templates/sql_app_errors.py                                                  |     8 |    0 |     8 |   100% |
| application_sdk/templates/sql_metadata_extractor.py                                          |    14 |    1 |    13 |    93% |
| application_sdk/templates/sql_query_extractor.py                                             |     6 |    1 |     5 |    83% |
| application_sdk/templates/contracts/__init__.py                                              |     1 |    0 |     1 |   100% |
| application_sdk/templates/contracts/base_metadata_extraction.py                              |     3 |    0 |     3 |   100% |
| application_sdk/templates/contracts/incremental_sql.py                                       |    26 |    5 |    21 |    81% |
| application_sdk/templates/contracts/sql_metadata.py                                          |    33 |    8 |    25 |    76% |
| application_sdk/templates/contracts/sql_query.py                                             |     7 |    0 |     7 |   100% |
| application_sdk/test_utils/integration/__init__.py                                           |     1 |    1 |     0 |     0% |
| application_sdk/testing/__init__.py                                                          |     1 |    0 |     1 |   100% |
| application_sdk/testing/_mustache.py                                                         |     2 |    0 |     2 |   100% |
| application_sdk/testing/fixtures.py                                                          |    10 |    0 |    10 |   100% |
| application_sdk/testing/mocks.py                                                             |    68 |   17 |    51 |    75% |
| application_sdk/testing/e2e/__init__.py                                                      |     1 |    0 |     1 |   100% |
| application_sdk/testing/e2e/_errors.py                                                       |    18 |    0 |    18 |   100% |
| application_sdk/testing/e2e/_poll.py                                                         |    13 |    0 |    13 |   100% |
| application_sdk/testing/e2e/base.py                                                          |    36 |    1 |    35 |    97% |
| application_sdk/testing/e2e/client.py                                                        |    61 |    6 |    55 |    90% |
| application_sdk/testing/e2e/config.py                                                        |     2 |    0 |     2 |   100% |
| application_sdk/testing/e2e/credential.py                                                    |     2 |    0 |     2 |   100% |
| application_sdk/testing/e2e/logs.py                                                          |     6 |    1 |     5 |    83% |
| application_sdk/testing/e2e/payload.py                                                       |     9 |    0 |     9 |   100% |
| application_sdk/testing/e2e/pods.py                                                          |     5 |    1 |     4 |    80% |
| application_sdk/testing/e2e/portforward.py                                                   |     4 |    0 |     4 |   100% |
| application_sdk/testing/e2e/sql_app.py                                                       |     9 |    0 |     9 |   100% |
| application_sdk/testing/e2e/substitutions.py                                                 |     3 |    0 |     3 |   100% |
| application_sdk/testing/e2e/workflows.py                                                     |     3 |    0 |     3 |   100% |
| application_sdk/testing/full_dag/__init__.py                                                 |     1 |    0 |     1 |   100% |
| application_sdk/testing/full_dag/_errors.py                                                  |     1 |    0 |     1 |   100% |
| application_sdk/testing/full_dag/base.py                                                     |    17 |    1 |    16 |    94% |
| application_sdk/testing/full_dag/client.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/testing/full_dag/payload.py                                                  |     8 |    0 |     8 |   100% |
| application_sdk/testing/full_dag/sql_app.py                                                  |     5 |    0 |     5 |   100% |
| application_sdk/testing/hypothesis/__init__.py                                               |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/__init__.py                                    |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/sql_client.py                                  |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/clients/__init__.py                            |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/clients/sql.py                                 |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/common/__init__.py                             |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/common/logger.py                               |     3 |    0 |     3 |   100% |
| application_sdk/testing/hypothesis/strategies/handlers/__init__.py                           |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/__init__.py                       |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/sql_metadata.py                   |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/sql_preflight.py                  |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/__init__.py                             |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/json_input.py                           |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/parquet_input.py                        |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/outputs/__init__.py                            |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/outputs/json_output.py                         |     2 |    1 |     1 |    50% |
| application_sdk/testing/hypothesis/strategies/outputs/statestore.py                          |     3 |    1 |     2 |    67% |
| application_sdk/testing/hypothesis/strategies/server/__init__.py                             |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/server/fastapi/__init__.py                     |     1 |    1 |     0 |     0% |
| application_sdk/testing/integration/__init__.py                                              |     1 |    0 |     1 |   100% |
| application_sdk/testing/integration/_errors.py                                               |     6 |    0 |     6 |   100% |
| application_sdk/testing/integration/assertions.py                                            |    55 |   25 |    30 |    55% |
| application_sdk/testing/integration/client.py                                                |    18 |    0 |    18 |   100% |
| application_sdk/testing/integration/comparison.py                                            |    12 |    1 |    11 |    92% |
| application_sdk/testing/integration/lazy.py                                                  |    10 |    0 |    10 |   100% |
| application_sdk/testing/integration/models.py                                                |     9 |    0 |     9 |   100% |
| application_sdk/testing/integration/runner.py                                                |    26 |    2 |    24 |    92% |
| application_sdk/testing/integration/source.py                                                |     8 |    0 |     8 |   100% |
| application_sdk/testing/integration/validation.py                                            |     7 |    0 |     7 |   100% |
| application_sdk/testing/parity/__init__.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/testing/parity/__main__.py                                                   |     2 |    1 |     1 |    50% |
| application_sdk/testing/parity/comparator.py                                                 |     8 |    0 |     8 |   100% |
| application_sdk/testing/parity/models.py                                                     |     5 |    1 |     4 |    80% |
| application_sdk/testing/parity/report.py                                                     |     4 |    0 |     4 |   100% |
| application_sdk/testing/scale_data_generator/__init__.py                                     |     1 |    0 |     1 |   100% |
| application_sdk/testing/scale_data_generator/config_loader.py                                |    11 |    4 |     7 |    64% |
| application_sdk/testing/scale_data_generator/data_generator.py                               |    10 |    3 |     7 |    70% |
| application_sdk/testing/scale_data_generator/driver.py                                       |     3 |    3 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/__init__.py                      |     1 |    1 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/base.py                          |     7 |    3 |     4 |    57% |
| application_sdk/testing/scale_data_generator/output_handler/csv_handler.py                   |     6 |    6 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/json_handler.py                  |     5 |    5 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/parquet_handler.py               |     6 |    6 |     0 |     0% |
| application_sdk/testing/sdr/__init__.py                                                      |     1 |    0 |     1 |   100% |
| application_sdk/testing/sdr/base.py                                                          |    14 |    3 |    11 |    79% |
| application_sdk/tools/__init__.py                                                            |     1 |    1 |     0 |     0% |
| application_sdk/tools/provision_credentials.py                                               |     2 |    1 |     1 |    50% |
| application_sdk/transformers/__init__.py                                                     |     4 |    2 |     2 |    50% |
| application_sdk/transformers/errors.py                                                       |     2 |    1 |     1 |    50% |
| application_sdk/transformers/atlas/__init__.py                                               |     6 |    1 |     5 |    83% |
| application_sdk/transformers/atlas/errors.py                                                 |     8 |    7 |     1 |    12% |
| application_sdk/transformers/atlas/sql.py                                                    |    25 |    4 |    21 |    84% |
| application_sdk/transformers/common/__init__.py                                              |     1 |    1 |     0 |     0% |
| application_sdk/transformers/common/last_sync.py                                             |     5 |    0 |     5 |   100% |
| application_sdk/transformers/common/utils.py                                                 |     6 |    0 |     6 |   100% |
| application_sdk/transformers/query/__init__.py                                               |    19 |    2 |    17 |    89% |
| application_sdk/transformers/query/errors.py                                                 |     5 |    3 |     2 |    40% |
| application_sdk/validation/__init__.py                                                       |     1 |    0 |     1 |   100% |
| application_sdk/validation/assets.py                                                         |    17 |    2 |    15 |    88% |
| contract-toolkit/examples/agent-e2e/app/generated/__init__.py                                |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_e2e_base.py                               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_e2e_credential.py                         |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_e2e_substitutions.py                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_input.py                                  |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/__init__.py                        |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/_e2e_base.py                       |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/_e2e_substitutions.py              |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/_input.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/__init__.py                           |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/_e2e_base.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/_e2e_credential.py                    |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/_input.py                             |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/__init__.py                             |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/_e2e_base.py                            |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/_e2e_substitutions.py                   |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/_input.py                               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/connection-ref/app/generated/__init__.py                           |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/connection-ref/app/generated/_e2e_base.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/connection-ref/app/generated/_input.py                             |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/__init__.py                                   |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/_e2e_base.py                                  |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/_e2e_substitutions.py                         |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/_input.py                                     |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/__init__.py                                    |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/_e2e_base.py                                   |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/_e2e_credential.py                             |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/_input.py                                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/__init__.py                                     |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_e2e_base.py                                    |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_e2e_credential.py                              |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_e2e_substitutions.py                           |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_input.py                                       |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/__init__.py                                  |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/_e2e_base.py                                 |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/_e2e_substitutions.py                        |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/_input.py                                    |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/__init__.py                                    |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/_e2e_base.py                                   |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/_e2e_substitutions.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/_input.py                                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/__init__.py                         |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_e2e_base.py                        |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_e2e_credential.py                  |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_e2e_substitutions.py               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_input.py                           |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/__init__.py                                |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/_e2e_base.py                               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/_e2e_substitutions.py                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/_input.py                                  |     2 |    2 |     0 |     0% |
| contract-toolkit/scripts/test-sdk-import.py                                                  |     5 |    1 |     4 |    80% |
| packages/conformance/conformance/__init__.py                                                 |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/cli.py                                                      |    14 |   12 |     2 |    14% |
| packages/conformance/conformance/bootstrap/__init__.py                                       |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/bootstrap/args.py                                           |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/bootstrap/autodetect.py                                     |    10 |    0 |    10 |   100% |
| packages/conformance/conformance/bootstrap/command.py                                        |    14 |    1 |    13 |    93% |
| packages/conformance/conformance/bootstrap/extract.py                                        |    24 |    0 |    24 |   100% |
| packages/conformance/conformance/bootstrap/render.py                                         |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/bootstrap/templates/build_conformance_args.py               |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/renovate/__init__.py                                        |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/renovate/classify.py                                        |    11 |    1 |    10 |    91% |
| packages/conformance/conformance/renovate/models.py                                          |    11 |    2 |     9 |    82% |
| packages/conformance/conformance/renovate/scan.py                                            |    10 |    6 |     4 |    40% |
| packages/conformance/conformance/scorecard/__init__.py                                       |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/scorecard/cli.py                                            |     7 |    3 |     4 |    57% |
| packages/conformance/conformance/scorecard/compute.py                                        |     9 |    3 |     6 |    67% |
| packages/conformance/conformance/scorecard/readers.py                                        |    11 |    0 |    11 |   100% |
| packages/conformance/conformance/scorecard/rubric.py                                         |    10 |    5 |     5 |    50% |
| packages/conformance/conformance/scorecard/schema.py                                         |    15 |    2 |    13 |    87% |
| packages/conformance/conformance/scorecard/validate.py                                       |     4 |    1 |     3 |    75% |
| packages/conformance/conformance/suite/__init__.py                                           |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/suite/runner.py                                             |     9 |    2 |     7 |    78% |
| packages/conformance/conformance/suite/checks/__init__.py                                    |     1 |    1 |     0 |     0% |
| packages/conformance/conformance/suite/checks/_entrypoint_contract_fields.py                 |    13 |    4 |     9 |    69% |
| packages/conformance/conformance/suite/checks/_sdk_contract_mixins.py                        |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/_toolkit_baseline.py                           |     6 |    1 |     5 |    83% |
| packages/conformance/conformance/suite/checks/_version.py                                    |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/actions_pinning.py                             |    10 |    0 |    10 |   100% |
| packages/conformance/conformance/suite/checks/asyncio_loop_scope.py                          |    13 |    2 |    11 |    85% |
| packages/conformance/conformance/suite/checks/bootstrap_drift.py                             |    12 |    1 |    11 |    92% |
| packages/conformance/conformance/suite/checks/coverage_config.py                             |    12 |    3 |     9 |    75% |
| packages/conformance/conformance/suite/checks/dependency_conformance.py                      |    38 |    0 |    38 |   100% |
| packages/conformance/conformance/suite/checks/dev_entrypoint.py                              |     6 |    0 |     6 |   100% |
| packages/conformance/conformance/suite/checks/dockerfile_conformance.py                      |    17 |    1 |    16 |    94% |
| packages/conformance/conformance/suite/checks/download_retry.py                              |    17 |    1 |    16 |    94% |
| packages/conformance/conformance/suite/checks/e2e_agent_spec.py                              |     8 |    1 |     7 |    88% |
| packages/conformance/conformance/suite/checks/e2e_deployment_name.py                         |     9 |    3 |     6 |    67% |
| packages/conformance/conformance/suite/checks/e2e_generated_harness.py                       |    22 |    8 |    14 |    64% |
| packages/conformance/conformance/suite/checks/e2e_workflow_shape.py                          |    17 |    4 |    13 |    76% |
| packages/conformance/conformance/suite/checks/generated_freshness.py                         |    23 |    0 |    23 |   100% |
| packages/conformance/conformance/suite/checks/gitignore_entries.py                           |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/suite/checks/integration_deselect.py                        |    11 |    2 |     9 |    82% |
| packages/conformance/conformance/suite/checks/integration_marking.py                         |    12 |    2 |    10 |    83% |
| packages/conformance/conformance/suite/checks/release_contract.py                            |     7 |    0 |     7 |   100% |
| packages/conformance/conformance/suite/checks/sdr.py                                         |    36 |    2 |    34 |    94% |
| packages/conformance/conformance/suite/checks/sdr_test_checks.py                             |    10 |    3 |     7 |    70% |
| packages/conformance/conformance/suite/checks/test_quality.py                                |    18 |    7 |    11 |    61% |
| packages/conformance/conformance/suite/checks/test_structure.py                              |    10 |    3 |     7 |    70% |
| packages/conformance/conformance/suite/checks/transform_templates.py                         |     9 |    1 |     8 |    89% |
| packages/conformance/conformance/suite/checks/_ast_common/__init__.py                        |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_cli.py                            |     6 |    3 |     3 |    50% |
| packages/conformance/conformance/suite/checks/_ast_common/_directives.py                     |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_discovery.py                      |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_findings.py                       |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_imports.py                        |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_io.py                             |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_pytest_collection.py              |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_sanitizers.py                     |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_scope.py                          |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_toml_suppress.py                  |     4 |    1 |     3 |    75% |
| packages/conformance/conformance/suite/checks/app_name_alignment/__init__.py                 |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/app_name_alignment/_check.py                   |     3 |    1 |     2 |    67% |
| packages/conformance/conformance/suite/checks/app_name_alignment/_code_app_name.py           |    12 |    0 |    12 |   100% |
| packages/conformance/conformance/suite/checks/app_name_alignment/_contract_app_name.py       |     8 |    0 |     8 |   100% |
| packages/conformance/conformance/suite/checks/client_seam/__init__.py                        |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/client_seam/_raw_http_to_atlan.py              |    10 |    0 |    10 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/__init__.py                        |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/_authoring.py                      |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/_consumer.py                       |     9 |    3 |     6 |    67% |
| packages/conformance/conformance/suite/checks/deprecation/_contract_compat.py                |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/_daft_runtime.py                   |    16 |    7 |     9 |    56% |
| packages/conformance/conformance/suite/checks/deprecation/_extractor.py                      |    19 |    5 |    14 |    74% |
| packages/conformance/conformance/suite/checks/deprecation/_ledger_schema.py                  |     8 |    1 |     7 |    88% |
| packages/conformance/conformance/suite/checks/deprecation/_manifest.py                       |    10 |    1 |     9 |    90% |
| packages/conformance/conformance/suite/checks/determinism/__init__.py                        |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/determinism/_p020_primitives.py                |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/determinism/_p021_io.py                        |     3 |    1 |     2 |    67% |
| packages/conformance/conformance/suite/checks/determinism/_p022_unawaited.py                 |     8 |    5 |     3 |    38% |
| packages/conformance/conformance/suite/checks/determinism/_p023_blocking_async.py            |    11 |    9 |     2 |    18% |
| packages/conformance/conformance/suite/checks/determinism/_p024_sync_atlan_client.py         |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/determinism/_p031_executor_offload.py          |     4 |    1 |     3 |    75% |
| packages/conformance/conformance/suite/checks/determinism/_p036_process_isolation.py         |     3 |    1 |     2 |    67% |
| packages/conformance/conformance/suite/checks/determinism/_workflow_methods.py               |     7 |    0 |     7 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/__init__.py                         |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/_bootstrap_common.py                |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/_server_bootstrap.py                |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/_worker_bootstrap.py                |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/__init__.py               |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/_check.py                 |     5 |    1 |     4 |    80% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/_code_entrypoints.py      |    11 |    0 |    11 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/_contract_entrypoints.py  |     5 |    1 |     4 |    80% |
| packages/conformance/conformance/suite/checks/error_handling/__init__.py                     |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/error_handling/_checker.py                     |    14 |   12 |     2 |    14% |
| packages/conformance/conformance/suite/checks/error_handling/_collect.py                     |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/error_handling/_constants.py                   |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/suite/checks/error_handling/_helpers.py                     |    21 |    3 |    18 |    86% |
| packages/conformance/conformance/suite/checks/error_handling/exception_chaining.py           |     5 |    3 |     2 |    40% |
|

This message was truncated. Download full message

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

📦 Trivy Vulnerability Scan Results

Schema Version Created At Artifact Type
2 2026-08-22T08:06:48.509519025Z . repository

Report Summary

Target Type Vulnerabilities packages/conformance/uv.lock
uv ✅ None found requirements.txt pip
✅ None found uv.lock uv ✅ None found

Scan Result Details

packages/conformance/uv.lock
requirements.txt
uv.lock

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

📦 Trivy Secret Scan Results

Schema Version Created At Artifact Type
2 2026-08-22T08:06:56.09079436Z . repository

Report Summary

Target Type Secrets packages/conformance/uv.lock
uv ✅ None found requirements.txt pip
✅ None found uv.lock uv ✅ None found

Scan Result Details

packages/conformance/uv.lock
requirements.txt
uv.lock

@atlan-app-fleet

atlan-app-fleet Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

☂️ Code Coverage

current status: ✅

Overall Coverage

Statements Covered Coverage Threshold Status
23056 21240 92% 0% 🟢

New Files

File Coverage Status
application_sdk/execution/_temporal/interceptors/sizing.py 100% 🟢
application_sdk/observability/cgroup.py 97% 🟢
application_sdk/observability/sizing.py 100% 🟢
application_sdk/observability/sizing_census.py 100% 🟢
application_sdk/observability/sizing_inputs.py 92% 🟢
application_sdk/observability/sizing_sink.py 94% 🟢
TOTAL 97% 🟢

Modified Files

File Coverage Status
application_sdk/constants.py 98% 🟢
application_sdk/execution/_temporal/worker.py 78% 🟢
application_sdk/execution/settings.py 100% 🟢
application_sdk/observability/observability.py 81% 🟢
application_sdk/storage/formats/utils.py 81% 🟢
TOTAL 88% 🟢

updated for commit: 7a9da91 by action🐍

OnkarVO7 and others added 7 commits August 19, 2026 19:38
Same code, less prose. Keeps the load-bearing warnings as one-liners — proven
reset vs version-sniffing, nulls not zeros, filter before the tracker, read the
trace after the tracker exits — and drops the surrounding exposition.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…able

Peak memory alone says a tier is wrong; it cannot say what to key the tier on.
Adds input_bytes / input_file_count / input_basis to the observation, plus an
activity.sizing.input_mib histogram and a peak_per_input_byte ratio.

Two sources, in order:

1. FileReference fields on the Input — zero config, and measured rather than
   reported. Sized in the interceptor's finally, not at entry: the SDK
   materialises durable refs at the top of the activity, so by then the bytes are
   on local disk and this is a stat instead of an object-store call per activity.
2. A sizing_input_bytes() hook on the Input — the escape hatch for apps that pass
   raw object-store paths. 53 org repos use FileReference, but AE is not one of
   them: merge takes input_prefixes: list[str] and would otherwise report nothing,
   which is the flagship case.

basis travels with the number because measured and self-reported bytes are not
the same quantity, and fitting one rule to a silent mix of them fits it to
neither. None means unknown, never 0 — a zero would fit a rule to inputs nobody
sized. The directory walk is capped and reports truncation rather than passing a
partial count off as complete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces the sizing_input_bytes() model hook with report_input_bytes(), and calls
it from _download_files — the one function every SDK Parquet/JSON read goes
through, covering both the local-hit and downloaded paths.

The hook had no usable consumer. AE's merge takes input_prefixes: list[str], has
no FileReference, and learns its byte counts inside the read loop rather than on
the Input model — so the flagship case could not have used it. Instrumenting the
reader instead means merge (and any app on those readers) contributes the driver
variable with no app code at all.

The collector is created by the interceptor and mutated in place, following
OutputInterceptor's pattern: a ContextVar *set* inside the activity may not be
visible to the interceptor across a thread or context boundary, whereas a shared
object is.

Reported bytes win over a FileReference walk — reported is what the activity read,
a ref is only what it was handed. Note ParquetFileReader is deprecated in favour
of FileReference, so the two sources cover today and where the SDK is heading.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…y pipeline

Adds a durable per-execution record. The OTel histograms answer 'is this tier
wrong'; fitting a rule that predicts a tier needs the rows, because a histogram
bucket cannot give you peak_per_input_byte per execution.

Rides AtlanObservability rather than adding a sink. That base already does
batching, hive partitioning by year/month/day/hour, gzipped NDJSON, retention
cleanup, and upload to the deployment store *and* the upstream Atlan store when
ENABLE_ATLAN_UPLOAD is set. NDJSON rather than a bespoke Parquet writer for the
same reason: ~400 lines of proven upload/partition/cleanup machinery already
exists, DuckDB reads gzipped NDJSON natively, and diverging would put this signal
somewhere no existing tooling looks.

That upstream leg also largely answers cross-tenant curation — records from every
tenant already land under one partitioned prefix — so app/deployment are stamped
on each row: a row that cannot name its tenant cannot fit that tenant's tiers, and
pooling tenants blindly would be wrong anyway since data volume is the thing being
measured.

Every row carries schema_version, because these are read months later mixed across
SDK versions and 'which keys are present' is not a contract.

Also fixes a latent trap the new signal exposed: LOCAL_OBS_SUBDIR_MAP and
OBSERVABILITY_S3_PREFIX_MAP are separate maps, and a signal in only one writes to
other/ on disk while uploading to sizing/. A test now asserts they agree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds started_at, pod, concurrency_max and is_attributable, and forbids the
memory.peak watermark reset whenever more than one activity is in flight.
Schema version 2 — a v1 row cannot say whether its peak was pod-wide, so v1 and
v2 must not be pooled.

A cgroup reading is pod-wide, and worker concurrency defaults to 100. Rather than
force concurrency to 1 — a throughput change on apps that have nothing to do with
tiering — each row now records the maximum concurrency it saw, so the analysis can
pick a model instead of pooling two: concurrency 1 fits per-activity, above 1 fits
per-pod by joining rows that overlap on (pod, started_at, duration). That join is
why the three fields are enough; no in-process bookkeeping of who-ran-with-whom is
needed.

Two correctness points found while building it:

- The census counts EVERY activity, not just allow-listed ones. What invalidates
  attribution is another activity using the pod's memory, not whether we were
  measuring it; counting only the allow-list would let a measured merge sharing a
  pod with unmeasured work report concurrency_max=1.
- leave() is idempotent and peak() reads without deregistering. Two callers release
  each execution, and an unconditional decrement undercounted concurrency for
  everything else still running.

Per-thread CPU is deliberately NOT included: loop.run_in_executor sits in the
innermost interceptor, so the whole interceptor chain runs on the event loop and
time.thread_time() there measures the loop, not the activity. Attributing it would
have been wrong in the direction that looks right.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng dataset

Two silent failures on the write path, both of which would have shown up only as
an empty prefix after a week of collecting nothing.

1. ATLAN_ENABLE_OBSERVABILITY_STORE_SINK gates logs, metrics and sizing together,
   and falls back to ATLAN_ENABLE_OBSERVABILITY_DAPR_SINK. AE sets that to false
   to stop shipping logs and metrics, so it resolved to false and _flush_records
   returned early — no local file, no upload. _store_sink_enabled() is now an
   overridable hook and the sizing sink returns True: collection is already gated
   twice, by APPLICATION_SDK_ENABLE_SIZING_TELEMETRY and the per-activity
   allow-list, so nothing is written unless an operator asked for it by name. Other
   signals still respect the flag; a test pins that.

2. _flush_records partitions on record["timestamp"], which process_record never
   emitted — KeyError per batch, swallowed as best-effort telemetry. Now set from
   the execution's started_at, so a row lands in the hour the activity ran rather
   than the hour it was flushed.

Both were found by one end-to-end flush test rather than by mocking the flush. It
asserts on the upload, not on a leftover local file: the base uploads and then
deletes the staged file, so a local-file check would only pass if the flush had
crashed before cleanup.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A full day of collection on three tenants wrote nothing to the object store. The
log lines were there and the measurements were sound, but every buffered row died
with its pod.

add_record only evaluates its flush condition when a record ARRIVES:

    len(buffer) >= batch_size or (now - last_flush) >= flush_interval

On this workload -- maxConcurrentActivities 1, a merge every 15-30 minutes, KEDA
scaling pods to zero in between -- a pod typically sees ONE record in its lifetime,
so neither branch is ever re-checked. AtlanTracesAdapter, AtlanLoggerAdapter and
AtlanMetricsAdapter all start _periodic_flush in __init__ for exactly this reason;
this sink inherited the base and skipped the one part of the pattern that makes the
interval real.

Three changes:
- start _periodic_flush, using the traces adaptor's loop-or-daemon-thread shape
- add drain(), for the gap between the last record and process exit
- promote the flush line to INFO. The base logs success at DEBUG, which every
  deployment filters, so whether the sink was writing could only be settled by
  exec-ing into a pod and forcing a flush by hand.

The new tests fail against the pre-fix __init__ and pass after it, verified by
reverting the fix and watching them go red -- a test for a silent bug that cannot
fail is worth nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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