Skip to content

feat(ci): add unit-tests gate to build-and-publish (DISTR-456)#1774

Merged
anurag-atlan merged 8 commits into
mainfrom
anuragbadoni/distr-456-unit-tests-gate
May 19, 2026
Merged

feat(ci): add unit-tests gate to build-and-publish (DISTR-456)#1774
anurag-atlan merged 8 commits into
mainfrom
anuragbadoni/distr-456-unit-tests-gate

Conversation

@anurag-atlan
Copy link
Copy Markdown
Contributor

@anurag-atlan anurag-atlan commented May 18, 2026

Summary

Lands the unit-tests gate for build-and-publish-app.yaml — first slice of DISTR-456 — App Certification Framework.

Refuses to publish a version unless the app's unit-tests workflow has run successfully on the publish SHA. The gate runs before any build minutes are spent so a failing test surfaces at minute 0 rather than after the multi-arch build.

Complements the in-flight cert framework on the marketplace side (atlanhq/global-marketplace#198) — image-side checks (image_vuln / secret_leak / sast_scan) run server-side via Temporal; this PR adds the CI-side gate that prevents broken-tests builds from ever reaching that pipeline.

2-phase rollout — this PR is phase 1 (safe to land today)

Phase PR What it does Risk
1 #1774 (this PR) Opt-in + warn-only gate. Apps that haven't set unit_tests_workflow_file skip the gate. Apps that have set it run the gate, get a warning if tests fail, but publish still proceeds. Zero breakage. Land anytime.
2 #1788 Flips unit_tests_workflow_file to required AND restores hard-block on gate failure. Laggard apps that haven't onboarded by then are publish-blocked until they do. Coordinated breakage. Merge when consumer adoption is high.

Combined effect of phase 1: no app's publish workflow will fail because of this gate, even after merging consumer PRs for apps with currently-broken tests. They'll see warnings on the workflow run — useful signal — but publish proceeds.

72 consumer draft PRs are already raised on the anuragbadoni/distr-456-unit-tests-gate branch. See the consumer-PR search.

How it works

build-and-publish-app.yaml (this PR)
        │
        ▼
┌──────────────────────────────────────────────────────────┐
│ Job: unit-tests-gate (NEW)                               │
│   if: inputs.publish && inputs.unit_tests_workflow_file  │
│       != ''  ← opt-in                                    │
│                                                          │
│ 1. Resolve SHA from inputs.ref or github.sha             │
│                                                          │
│ 2. Look for existing run of `unit_tests_workflow_file`   │
│    on this commit (any conclusion):                      │
│      - found        → capture run_id, skip dispatch      │
│      - not found    → dispatch on the publish ref,       │
│                       poll up to 60s for new run_id      │
│                                                          │
│ 3. Poll the run by ID every 25s until status=completed   │
│                                                          │
│ 4. If conclusion != success → emit ::warning:: only      │
│    (warn-only mode — phase 1)                            │
│    The enforcement-flip PR #1788 restores exit 1 here.   │
└──────────────────────────────────────────────────────────┘
        │
        ▼ (always)
prepare → build → merge → security-scan → publish

The gate tracks runs by ID, not by check name — apps don't need any particular naming convention on their test jobs.

Caller contract (when opting in)

To opt an app into the gate, set the input in build-and-publish.yaml:

uses: atlanhq/application-sdk/.github/workflows/build-and-publish-app.yaml@main
with:
  publish: ${{ ... }}
  unit_tests_workflow_file: "unit-tests.yml"   # ← opt in

And declare workflow_dispatch: in the tests workflow's on: block so the gate can fire fresh runs on commits that haven't been independently tested.

See docs/standards/unit-tests-gate.md for the full contract + reference template.

Behaviour matrix (phase 1)

State Gate behaviour Publish
Input unset (opt-out) Gate job is skipped entirely ✅ Proceeds
Input set, run exists on SHA, succeeded Reuse, pass ✅ Proceeds
Input set, run exists on SHA, failed Reuse, ⚠️ warning emitted ✅ Proceeds (warn-only)
Input set, run exists on SHA, in progress Poll the existing run ✅ Proceeds on success / ⚠️ warning on failure
Input set, no run on SHA Dispatch fresh on publish ref, then wait ✅ Proceeds on success / ⚠️ warning on failure
Input set, workflow file missing / lacks workflow_dispatch: Pre-flight fails with onboarding error ❌ Publish blocked (this is the only failure case in phase 1)

Validation

Tested end-to-end on two real apps via test branches (not merged):

Path Pilot app Run Result
Existing successful run on SHA atlan-clickhouse-app #26077809792 ✅ Reused 28-day-old run, passed in seconds
Fresh dispatch + success atlan-clickhouse-app #26078694929 ✅ Dispatched, waited, passed
Fresh dispatch + failure (pre-warn-only) atlan-clickhouse-app #26078771430 ✅ Gate aborted publish at minute 1 (this validates the eventual phase-2 behavior)
Multi-job workflow (checks.yml) atlan-publish-app #26080651958 ✅ Dispatched PR Checks (4 jobs), all passed, publish proceeded

Rollout (recommended sequence)

  1. Merge this PR today — safe, non-breaking. Existing callers continue working unchanged.
  2. Merge the 72 consumer PRs on the anuragbadoni/distr-456-unit-tests-gate branch at each consumer's own pace. Each PR sets unit_tests_workflow_file: and adds workflow_dispatch: to the tests workflow. Apps with currently-failing tests can be safely merged — they'll emit warnings but won't break.
  3. Land enforcement-flip #1788 — once adoption is high. Flips unit_tests_workflow_file to required + restores hard-block on gate failure.

What's in the diff

  • .github/workflows/build-and-publish-app.yaml
    • New optional input unit_tests_workflow_file (default "").
    • New checks: read permission for the gate's API calls.
    • New unit-tests-gate job (resolve → find-or-dispatch → wait → warn), gated by if: inputs.publish && inputs.unit_tests_workflow_file != ''.
    • Phase-1 warn-only on the block step — emits ::warning::, does not exit 1. PR refactor(ci): enforce unit-tests gate — workflow_file required (DISTR-456 follow-up) #1788 reverts this.
    • prepare.needs extended to [validate-channel, unit-tests-gate]. The existing if: !failure() && !cancelled() already handles the skipped-on-build-only and skipped-on-opt-out cases correctly.
  • docs/standards/unit-tests-gate.md (new)
    • The contract every consumer repo must satisfy when opting in.
    • Reference template .github/workflows/unit-tests.yml.

No third-party actions added. The gate uses inline gh api / gh run view / gh workflow run calls.

Out of scope (follow-ups)

  • Enforcement flip: #1788
  • Sending coverage % into the marketplace publish payload (depends on GM #198)
  • Pulse-portal comms post
  • Cert-framework test_coverage activity (Aditya's scope)

Block publish unless the consumer repo's Tests workflow has reported a
successful `unit-tests` check (or whatever name is passed via
`unit_tests_check_name`) for the SHA being published. The gate runs
before any build minutes are spent so failing tests or missing-coverage
checks surface at minute 0 rather than after the multi-arch build.

Behaviour:
- Check absent on SHA  -> fail with onboarding instructions
- Check still running  -> poll every 30s, max 30 min
- Check failed         -> fail with the run's conclusion
- Check succeeded      -> proceed to prepare / build / publish

Only runs when `publish: true`; build-only invocations are unaffected.

Adds `checks: read` to the workflow permissions for the Checks API
lookups. The wait action is pinned to its v1.2.0 SHA per the
supply-chain rules in CLAUDE.md.

Contract and reference template are documented in
docs/standards/unit-tests-gate.md.
@snykgituser
Copy link
Copy Markdown

snykgituser commented May 18, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

📜 Docstring Coverage Report

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

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                     |    21 |    2 |    19 |    90% |
| application_sdk/__init__.py                                                    |     1 |    0 |     1 |   100% |
| application_sdk/_discovery_errors.py                                           |     7 |    0 |     7 |   100% |
| application_sdk/constants.py                                                   |     3 |    1 |     2 |    67% |
| application_sdk/discovery.py                                                   |    12 |    3 |     9 |    75% |
| application_sdk/main.py                                                        |    31 |    6 |    25 |    81% |
| application_sdk/main_errors.py                                                 |     5 |    0 |     5 |   100% |
| application_sdk/version.py                                                     |     1 |    0 |     1 |   100% |
| application_sdk/app/__init__.py                                                |     1 |    0 |     1 |   100% |
| application_sdk/app/base.py                                                    |    67 |   13 |    54 |    81% |
| application_sdk/app/base_errors.py                                             |     5 |    0 |     5 |   100% |
| application_sdk/app/client.py                                                  |     1 |    0 |     1 |   100% |
| application_sdk/app/context.py                                                 |    38 |    2 |    36 |    95% |
| application_sdk/app/entrypoint.py                                              |    13 |    4 |     9 |    69% |
| application_sdk/app/registry.py                                                |    37 |   11 |    26 |    70% |
| application_sdk/app/task.py                                                    |    14 |    6 |     8 |    57% |
| 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                                     |    10 |    4 |     6 |    60% |
| 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/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                                               |     6 |    0 |     6 |   100% |
| application_sdk/common/file_converter.py                                       |     9 |    5 |     4 |    44% |
| application_sdk/common/file_ops.py                                             |    16 |    1 |    15 |    94% |
| application_sdk/common/models.py                                               |     4 |    2 |     2 |    50% |
| application_sdk/common/path.py                                                 |     2 |    1 |     1 |    50% |
| application_sdk/common/sql_filters.py                                          |     9 |    0 |     9 |   100% |
| application_sdk/common/sql_filters_errors.py                                   |     2 |    0 |     2 |   100% |
| application_sdk/common/transforms.py                                           |     5 |    0 |     5 |   100% |
| application_sdk/common/types.py                                                |     2 |    1 |     1 |    50% |
| 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                                  |    12 |    1 |    11 |    92% |
| 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/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                                           |     7 |    1 |     6 |    86% |
| 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/oauth.py                                           |    10 |    2 |     8 |    80% |
| application_sdk/credentials/ref.py                                             |    16 |    1 |    15 |    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                                           |     3 |    0 |     3 |   100% |
| application_sdk/dev/__init__.py                                                |     1 |    0 |     1 |   100% |
| application_sdk/dev/_dapr.py                                                   |    11 |    2 |     9 |    82% |
| application_sdk/dev/_dapr_errors.py                                            |     5 |    4 |     1 |    20% |
| application_sdk/dev/_embedded.py                                               |     3 |    0 |     3 |   100% |
| application_sdk/errors/__init__.py                                             |     4 |    1 |     3 |    75% |
| application_sdk/errors/base.py                                                 |     8 |    2 |     6 |    75% |
| application_sdk/errors/categories.py                                           |     3 |    0 |     3 |   100% |
| application_sdk/errors/leaves.py                                               |    15 |    8 |     7 |    47% |
| application_sdk/errors/wire.py                                                 |     3 |    1 |     2 |    67% |
| 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/retry.py                                             |     7 |    0 |     7 |   100% |
| application_sdk/execution/sandbox.py                                           |     4 |    0 |     4 |   100% |
| application_sdk/execution/settings.py                                          |     6 |    1 |     5 |    83% |
| 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                        |     7 |    0 |     7 |   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                              |     7 |    0 |     7 |   100% |
| application_sdk/execution/_temporal/activity_utils.py                          |     5 |    0 |     5 |   100% |
| application_sdk/execution/_temporal/auth.py                                    |    12 |    0 |    12 |   100% |
| application_sdk/execution/_temporal/backend.py                                 |    10 |    1 |     9 |    90% |
| 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/sdr.py                                     |    13 |    7 |     6 |    46% |
| application_sdk/execution/_temporal/worker.py                                  |    10 |    5 |     5 |    50% |
| 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/lock.py                       |    10 |    2 |     8 |    80% |
| application_sdk/execution/_temporal/interceptors/log.py                        |    18 |   12 |     6 |    33% |
| application_sdk/execution/_temporal/interceptors/metrics.py                    |    14 |   12 |     2 |    14% |
| application_sdk/execution/_temporal/interceptors/outputs.py                    |     9 |    0 |     9 |   100% |
| 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                                             |    17 |    5 |    12 |    71% |
| application_sdk/handler/contracts.py                                           |    32 |    3 |    29 |    91% |
| application_sdk/handler/manifest.py                                            |     5 |    0 |     5 |   100% |
| application_sdk/handler/service.py                                             |    43 |   23 |    20 |    47% |
| 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                                      |    21 |    6 |    15 |    71% |
| 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                                 |    28 |    4 |    24 |    86% |
| application_sdk/infrastructure/_dapr/credential_vault.py                       |    13 |    4 |     9 |    69% |
| application_sdk/infrastructure/_dapr/http.py                                   |    17 |   12 |     5 |    29% |
| 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/context.py                                       |     4 |    0 |     4 |   100% |
| application_sdk/observability/correlation.py                                   |     6 |    0 |     6 |   100% |
| application_sdk/observability/logger_adaptor.py                                |    45 |    8 |    37 |    82% |
| 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                                 |    19 |    4 |    15 |    79% |
| 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                              |     5 |    0 |     5 |   100% |
| application_sdk/observability/segment_client.py                                |    15 |    1 |    14 |    93% |
| 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/health.py                                               |    20 |    0 |    20 |   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                                           |     5 |    0 |     5 |   100% |
| 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/_obstore_config.py                                     |     5 |    0 |     5 |   100% |
| application_sdk/storage/batch.py                                               |     8 |    2 |     6 |    75% |
| application_sdk/storage/binding.py                                             |     4 |    0 |     4 |   100% |
| application_sdk/storage/cloud.py                                               |    22 |    5 |    17 |    77% |
| application_sdk/storage/errors.py                                              |    19 |   12 |     7 |    37% |
| application_sdk/storage/factory.py                                             |     3 |    0 |     3 |   100% |
| application_sdk/storage/file_ref_sync.py                                       |    13 |    3 |    10 |    77% |
| application_sdk/storage/ops.py                                                 |    21 |    1 |    20 |    95% |
| application_sdk/storage/reference.py                                           |    11 |    1 |    10 |    91% |
| application_sdk/storage/rolling.py                                             |    31 |   12 |    19 |    61% |
| application_sdk/storage/rolling_errors.py                                      |     4 |    0 |     4 |   100% |
| application_sdk/storage/transfer.py                                            |    13 |    3 |    10 |    77% |
| application_sdk/storage/formats/__init__.py                                    |    29 |    0 |    29 |   100% |
| application_sdk/storage/formats/format_errors.py                               |    19 |    0 |    19 |   100% |
| application_sdk/storage/formats/json.py                                        |    15 |    1 |    14 |    93% |
| application_sdk/storage/formats/parquet.py                                     |    27 |    1 |    26 |    96% |
| application_sdk/storage/formats/utils.py                                       |     9 |    2 |     7 |    78% |
| application_sdk/templates/__init__.py                                          |     2 |    1 |     1 |    50% |
| application_sdk/templates/_template_errors.py                                  |     4 |    0 |     4 |   100% |
| application_sdk/templates/base_metadata_extractor.py                           |     4 |    1 |     3 |    75% |
| application_sdk/templates/incremental_sql_metadata_extractor.py                |    18 |    1 |    17 |    94% |
| application_sdk/templates/sql_app.py                                           |    29 |    0 |    29 |   100% |
| application_sdk/templates/sql_app_errors.py                                    |     7 |    0 |     7 |   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                         |    20 |    0 |    20 |   100% |
| application_sdk/templates/contracts/sql_metadata.py                            |    26 |    4 |    22 |    85% |
| 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/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/config.py                                          |     2 |    0 |     2 |   100% |
| application_sdk/testing/e2e/logs.py                                            |     6 |    1 |     5 |    83% |
| application_sdk/testing/e2e/pods.py                                            |     5 |    1 |     4 |    80% |
| application_sdk/testing/e2e/portforward.py                                     |     4 |    0 |     4 |   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                                    |    10 |    0 |    10 |   100% |
| application_sdk/testing/full_dag/base.py                                       |    15 |    1 |    14 |    93% |
| application_sdk/testing/full_dag/client.py                                     |    32 |    6 |    26 |    81% |
| 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                                 |     4 |    0 |     4 |   100% |
| application_sdk/testing/integration/assertions.py                              |    55 |   25 |    30 |    55% |
| application_sdk/testing/integration/client.py                                  |    16 |    0 |    16 |   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                                  |    24 |    2 |    22 |    92% |
| 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                                            |     5 |    2 |     3 |    60% |
| application_sdk/tools/__init__.py                                              |     1 |    1 |     0 |     0% |
| application_sdk/tools/provision_credentials.py                                 |     2 |    1 |     1 |    50% |
| application_sdk/transformers/__init__.py                                       |     3 |    1 |     2 |    67% |
| 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                                 |    11 |    2 |     9 |    82% |
| application_sdk/transformers/query/errors.py                                   |     4 |    3 |     1 |    25% |
| contract-toolkit/examples/connection-ref/generated/__init__.py                 |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/connection-ref/generated/_input.py                   |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/fanin/generated/__init__.py                          |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/fanin/generated/_input.py                            |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/openapi/generated/__init__.py                        |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/openapi/generated/_input.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/postgres/generated/__init__.py                       |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/postgres/generated/_input.py                         |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/publish-controls/generated/__init__.py               |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/publish-controls/generated/_input.py                 |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/trino/generated/__init__.py                          |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/trino/generated/_input.py                            |     2 |    2 |     0 |     0% |
| contract-toolkit/scripts/test-sdk-import.py                                    |     3 |    1 |     2 |    67% |
| examples/application_custom_fastapi.py                                         |    10 |    5 |     5 |    50% |
| examples/application_fastapi.py                                                |    10 |    1 |     9 |    90% |
| examples/application_hello_world.py                                            |     6 |    0 |     6 |   100% |
| examples/application_sql.py                                                    |    11 |    3 |     8 |    73% |
| examples/application_sql_miner.py                                              |     9 |    3 |     6 |    67% |
| examples/application_sql_with_custom_transformer.py                            |    12 |    7 |     5 |    42% |
| examples/run_examples.py                                                       |    13 |   12 |     1 |     8% |
| tests/__init__.py                                                              |     1 |    1 |     0 |     0% |
| tests/conftest.py                                                              |     1 |    0 |     1 |   100% |
| tests/e2e/__init__.py                                                          |     1 |    1 |     0 |     0% |
| tests/e2e/conftest.py                                                          |     4 |    1 |     3 |    75% |
| tests/integration/__init__.py                                                  |     1 |    0 |     1 |   100% |
| tests/integration/conftest.py                                                  |    14 |    2 |    12 |    86% |
| tests/integration/test_cloud_store.py                                          |    15 |   12 |     3 |    20% |
| tests/integration/test_core_execution.py                                       |    29 |   24 |     5 |    17% |
| tests/integration/test_dapr_http.py                                            |    22 |    0 |    22 |   100% |
| tests/integration/test_error_and_retry.py                                      |    19 |   15 |     4 |    21% |
| tests/integration/test_events_serde.py                                         |    15 |    5 |    10 |    67% |
| tests/integration/test_handler_service.py                                      |    60 |   31 |    29 |    48% |
| tests/integration/test_heartbeat.py                                            |    13 |   10 |     3 |    23% |
| tests/integration/test_incremental_pipeline.py                                 |    14 |    3 |    11 |    79% |
| tests/integration/test_lifecycle.py                                            |    21 |   17 |     4 |    19% |
| tests/integration/test_multi_entrypoint.py                                     |    26 |   19 |     7 |    27% |
| tests/integration/test_output_e2e.py                                           |    16 |    0 |    16 |   100% |
| tests/integration/test_storage_io.py                                           |    19 |    0 |    19 |   100% |
| tests/integration/test_storage_writers.py                                      |     9 |    2 |     7 |    78% |
| tests/integration/test_timeout.py                                              |     7 |    5 |     2 |    29% |
| tests/integration/_example/__init__.py                                         |     1 |    0 |     1 |   100% |
| tests/integration/_example/conftest.py                                         |     6 |    0 |     6 |   100% |
| tests/integration/_example/scenarios.py                                        |     1 |    0 |     1 |   100% |
| tests/integration/_example/test_integration.py                                 |     2 |    0 |     2 |   100% |
| tests/unit/__init__.py                                                         |     1 |    1 |     0 |     0% |
| tests/unit/conftest.py                                                         |     6 |    1 |     5 |    83% |
| tests/unit/test_app_config.py                                                  |    82 |   45 |    37 |    45% |
| tests/unit/test_discovery.py                                                   |    75 |   69 |     6 |     8% |
| tests/unit/test_imports.py                                                     |     4 |    0 |     4 |   100% |
| tests/unit/test_main.py                                                        |   131 |   60 |    71 |    54% |
| tests/unit/test_parse_atlan_yaml.py                                            |    29 |   25 |     4 |    14% |
| tests/unit/app/__init__.py                                                     |     1 |    1 |     0 |     0% |
| tests/unit/app/test_base.py                                                    |   196 |  155 |    41 |    21% |
| tests/unit/app/test_cleanup_files.py                                           |    15 |   14 |     1 |     7% |
| tests/unit/app/test_cleanup_storage.py                                         |    29 |   26 |     3 |    10% |
| tests/unit/app/test_client.py                                                  |    19 |    1 |    18 |    95% |
| tests/unit/app/test_context.py                                                 |    84 |   70 |    14 |    17% |
| tests/unit/app/test_entrypoint.py                                              |    65 |   38 |    27 |    42% |
| tests/unit/app/test_on_complete.py                                             |    35 |   32 |     3 |     9% |
| tests/unit/app/test_registry.py                                                |    34 |    4 |    30 |    88% |
| tests/unit/app/test_task.py                                                    |    76 |   46 |    30 |    39% |
| tests/unit/clients/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/clients/test_async_sql_client.py                                    |    11 |   10 |     1 |     9% |
| tests/unit/clients/test_azure_auth.py                                          |    33 |    0 |    33 |   100% |
| tests/unit/clients/test_azure_client.py                                        |    19 |    0 |    19 |   100% |
| tests/unit/clients/test_azure_client_contracts.py                              |    52 |   46 |     6 |    12% |
| tests/unit/clients/test_base_client.py                                         |    23 |    1 |    22 |    96% |
| tests/unit/clients/test_clienterror_preservation.py                            |    13 |    8 |     5 |    38% |
| tests/unit/clients/test_redis_client.py                                        |    40 |    0 |    40 |   100% |
| tests/unit/clients/test_redis_client_contracts.py                              |    56 |   36 |    20 |    36% |
| tests/unit/clients/test_sql_client.py                                          |    63 |   26 |    37 |    59% |
| tests/unit/clients/test_sql_typecasters.py                                     |    22 |   16 |     6 |    27% |
| tests/unit/clients/test_ssl_utils.py                                           |    41 |    4 |    37 |    90% |
| tests/unit/common/test_aws_utils.py                                            |    30 |    1 |    29 |    97% |
| tests/unit/common/test_column_extraction.py                                    |    11 |    0 |    11 |   100% |
| tests/unit/common/test_env_warnings.py                                         |    10 |    7 |     3 |    30% |
| tests/unit/common/test_file_converter.py                                       |    29 |    0 |    29 |   100% |
| tests/unit/common/test_file_ops.py                                             |    21 |    0 |    21 |   100% |
| tests/unit/common/test_path.py                                                 |     6 |    0 |     6 |   100% |
| tests/unit/common/test_transforms.py                                           |    34 |   31 |     3 |     9% |
| tests/unit/common/test_utils.py                                                |    82 |   11 |    71 |    87% |
| tests/unit/common/test_utils_file_discovery.py                                 |    13 |    0 |    13 |   100% |
| tests/unit/common/incremental/__init__.py                                      |     1 |    1 |     0 |     0% |
| tests/unit/common/incremental/test_helpers.py                                  |    37 |    1 |    36 |    97% |
| tests/unit/common/incremental/test_incremental_diff.py                         |    24 |   15 |     9 |    38% |
| tests/unit/common/incremental/test_marker.py                                   |    16 |    0 |    16 |   100% |
| tests/unit/common/incremental/test_models.py                                   |    15 |    0 |    15 |   100% |
| tests/unit/common/incremental/test_state_reader.py                             |     7 |    1 |     6 |    86% |
| tests/unit/common/incremental/test_state_writer.py                             |    32 |    2 |    30 |    94% |
| tests/unit/common/incremental/column_extraction/__init__.py                    |     1 |    1 |     0 |     0% |
| tests/unit/common/incremental/column_extraction/test_analysis.py               |    16 |   10 |     6 |    38% |
| tests/unit/common/incremental/column_extraction/test_backfill.py               |    24 |    5 |    19 |    79% |
| tests/unit/common/incremental/state/__init__.py                                |     1 |    1 |     0 |     0% |
| tests/unit/common/incremental/state/test_table_scope.py                        |    36 |   26 |    10 |    28% |
| tests/unit/contracts/__init__.py                                               |     1 |    1 |     0 |     0% |
| tests/unit/contracts/test_base.py                                              |   136 |  133 |     3 |     2% |
| tests/unit/contracts/test_connection_ref.py                                    |    31 |   29 |     2 |     6% |
| tests/unit/contracts/test_git_reference.py                                     |    23 |   22 |     1 |     4% |
| tests/unit/contracts/test_storage_tier_temporal_serde.py                       |     9 |    1 |     8 |    89% |
| tests/unit/contracts/test_types.py                                             |    25 |   24 |     1 |     4% |
| tests/unit/credentials/__init__.py                                             |     1 |    1 |     0 |     0% |
| tests/unit/credentials/test_agent.py                                           |    88 |   59 |    29 |    33% |
| tests/unit/credentials/test_atlan.py                                           |    41 |   31 |    10 |    24% |
| tests/unit/credentials/test_atlan_client.py                                    |    29 |   22 |     7 |    24% |
| tests/unit/credentials/test_mock_store.py                                      |    17 |   15 |     2 |    12% |
| tests/unit/credentials/test_oauth.py                                           |    19 |   15 |     4 |    21% |
| tests/unit/credentials/test_ref.py                                             |    26 |   23 |     3 |    12% |
| tests/unit/credentials/test_registry.py                                        |    20 |   18 |     2 |    10% |
| tests/unit/credentials/test_resolver.py                                        |    21 |    9 |    12 |    57% |
| tests/unit/credentials/test_types.py                                           |    42 |   41 |     1 |     2% |
| tests/unit/credentials/test_utils.py                                           |    18 |    1 |    17 |    94% |
| tests/unit/decorators/__init__.py                                              |     1 |    1 |     0 |     0% |
| tests/unit/decorators/test_mcp_tool.py                                         |    56 |    4 |    52 |    93% |
| tests/unit/dev/__init__.py                                                     |     1 |    1 |     0 |     0% |
| tests/unit/dev/test_dapr.py                                                    |    21 |   16 |     5 |    24% |
| tests/unit/dev/test_embedded.py                                                |     4 |    1 |     3 |    75% |
| tests/unit/errors/__init__.py                                                  |     1 |    1 |     0 |     0% |
| tests/unit/errors/test_app_subclassing.py                                      |     9 |    8 |     1 |    11% |
| tests/unit/errors/test_back_compat.py                                          |   102 |  101 |     1 |     1% |
| tests/unit/errors/test_base.py                                                 |    21 |   20 |     1 |     5% |
| tests/unit/errors/test_categorical.py                                          |     7 |    6 |     1 |    14% |
| tests/unit/errors/test_domain_evidence.py                                      |    12 |   10 |     2 |    17% |
| tests/unit/execution/__init__.py                                               |     1 |    1 |     0 |     0% |
| tests/unit/execution/conftest.py                                               |     4 |    2 |     2 |    50% |
| tests/unit/execution/test_activities.py                                        |   119 |  105 |    14 |    12% |
| tests/unit/execution/test_activities_tracking.py                               |    18 |   12 |     6 |    33% |
| tests/unit/execution/test_auth_token_refresh_event.py                          |    61 |   35 |    26 |    43% |
| tests/unit/execution/test_backend.py                                           |    37 |   31 |     6 |    16% |
| tests/unit/execution/test_converter.py                                         |    15 |   12 |     3 |    20% |
| tests/unit/execution/test_eviction.py                                          |    31 |   27 |     4 |    13% |
| tests/unit/execution/test_heartbeat.py                                         |    32 |   18 |    14 |    44% |
| tests/unit/execution/test_lock_interceptor.py                                  |    33 |   13 |    20 |    61% |
| tests/unit/execution/test_run_in_thread.py                                     |     6 |    1 |     5 |    83% |
| tests/unit/execution/test_sdr.py                                               |    31 |   25 |     6 |    19% |
| tests/unit/execution/test_settings.py                                          |    28 |   23 |     5 |    18% |
| tests/unit/execution/test_temporal_prometheus.py                               |     7 |    0 |     7 |   100% |
| tests/unit/execution/test_worker.py                                            |    72 |   56 |    16 |    22% |
| tests/unit/execution/test_workflows.py                                         |    44 |   37 |     7 |    16% |
| tests/unit/handler/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/handler/test_base.py                                                |    18 |   17 |     1 |     6% |
| tests/unit/handler/test_contracts.py                                           |    63 |   44 |    19 |    30% |
| tests/unit/handler/test_service.py                                             |   330 |  219 |   111 |    34% |
| tests/unit/infrastructure/__init__.py                                          |     1 |    1 |     0 |     0% |
| tests/unit/infrastructure/test_bindings.py                                     |    18 |   14 |     4 |    22% |
| tests/unit/infrastructure/test_capacity.py                                     |    20 |   16 |     4 |    20% |
| tests/unit/infrastructure/test_connection_pool_config.py                       |    13 |    1 |    12 |    92% |
| tests/unit/infrastructure/test_credential_state_store.py                       |    19 |   12 |     7 |    37% |
| tests/unit/infrastructure/test_credential_vault.py                             |    36 |   17 |    19 |    53% |
| tests/unit/infrastructure/test_dapr_http.py                                    |    45 |   27 |    18 |    40% |
| tests/unit/infrastructure/test_dapr_wrappers.py                                |    46 |   44 |     2 |     4% |
| tests/unit/infrastructure/test_pubsub.py                                       |    30 |   10 |    20 |    67% |
| tests/unit/infrastructure/test_secrets.py                                      |    36 |    0 |    36 |   100% |
| tests/unit/infrastructure/test_state.py                                        |    20 |    0 |    20 |   100% |
| tests/unit/interceptors/__init__.py                                            |     1 |    1 |     0 |     0% |
| tests/unit/interceptors/test_events.py                                         |    42 |   29 |    13 |    31% |
| tests/unit/interceptors/test_log_interceptor.py                                |    69 |   64 |     5 |     7% |
| tests/unit/interceptors/test_metrics_interceptor.py                            |    30 |   29 |     1 |     3% |
| tests/unit/interceptors/test_output_interceptor.py                             |    35 |    3 |    32 |    91% |
| tests/unit/interceptors/test_trace_interceptor.py                              |    17 |   16 |     1 |     6% |
| tests/unit/observability/__init__.py                                           |     1 |    1 |     0 |     0% |
| tests/unit/observability/test_execution_context.py                             |    11 |    0 |    11 |   100% |
| tests/unit/observability/test_logger_adaptor.py                                |   140 |   62 |    78 |    56% |
| tests/unit/observability/test_metrics.py                                       |    11 |   10 |     1 |     9% |
| tests/unit/observability/test_metrics_adaptor.py                               |    29 |    6 |    23 |    79% |
| tests/unit/observability/test_objectstore_metric_exporter.py                   |    17 |   13 |     4 |    24% |
| tests/unit/observability/test_observability_decorator.py                       |    31 |   27 |     4 |    13% |
| tests/unit/observability/test_prometheus_enrichment.py                         |    13 |    8 |     5 |    38% |
| tests/unit/observability/test_pushgateway.py                                   |    50 |   42 |     8 |    16% |
| tests/unit/observability/test_resource_sampler.py                              |    12 |    7 |     5 |    42% |
| tests/unit/observability/test_segment_client.py                                |    65 |   50 |    15 |    23% |
| tests/unit/observability/test_traces_adaptor.py                                |    13 |    1 |    12 |    92% |
| tests/unit/observability/test_traces_adaptor_contracts.py                      |    27 |   22 |     5 |    19% |
| tests/unit/outputs/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/outputs/test_outputs.py                                             |    33 |   24 |     9 |    27% |
| tests/unit/server/__init__.py                                                  |     1 |    1 |     0 |     0% |
| tests/unit/server/test_health.py                                               |    24 |   21 |     3 |    12% |
| tests/unit/server/fastapi/test_fastapi_utils.py                                |    36 |    0 |    36 |   100% |
| tests/unit/server/mcp/__init__.py                                              |     1 |    1 |     0 |     0% |
| tests/unit/server/mcp/test_mcp_server_v3.py                                    |    14 |    9 |     5 |    36% |
| tests/unit/storage/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/storage/test_batch.py                                               |    36 |   26 |    10 |    28% |
| tests/unit/storage/test_binding.py                                             |    26 |   19 |     7 |    27% |
| tests/unit/storage/test_cloud.py                                               |    45 |   35 |    10 |    22% |
| tests/unit/storage/test_file_ref_sync.py                                       |    37 |   28 |     9 |    24% |
| tests/unit/storage/test_obstore_config.py                                      |    19 |   15 |     4 |    21% |
| tests/unit/storage/test_ops.py                                                 |    98 |   78 |    20 |    20% |
| tests/unit/storage/test_path_separators.py                                     |    13 |    0 |    13 |   100% |
| tests/unit/storage/test_reference.py                                           |    51 |   31 |    20 |    39% |
| tests/unit/storage/test_rolling.py                                             |    68 |   53 |    15 |    22% |
| tests/unit/storage/test_transfer.py                                            |    40 |   32 |     8 |    20% |
| tests/unit/storage/formats/__init__.py                                         |     1 |    1 |     0 |     0% |
| tests/unit/storage/formats/test_base_io.py                                     |    60 |   53 |     7 |    12% |
| tests/unit/storage/formats/test_reader_isolation.py                            |     8 |    2 |     6 |    75% |
| tests/unit/storage/formats/test_utils.py                                       |     2 |    2 |     0 |     0% |
| tests/unit/storage/formats/test_writer_data_integrity.py                       |    26 |    6 |    20 |    77% |
| tests/unit/storage/formats/readers/__init__.py                                 |     1 |    1 |     0 |     0% |
| tests/unit/storage/formats/readers/test_json_reader.py                         |    37 |   18 |    19 |    51% |
| tests/unit/storage/formats/readers/test_parquet_reader.py                      |    60 |   38 |    22 |    37% |
| tests/unit/storage/formats/writers/__init__.py                                 |     1 |    1 |     0 |     0% |
| tests/unit/storage/formats/writers/test_json_writer.py                         |     7 |    6 |     1 |    14% |
| tests/unit/storage/formats/writers/test_parquet_writer.py                      |    74 |   11 |    63 |    85% |
| tests/unit/templates/__init__.py                                               |     1 |    1 |     0 |     0% |
| tests/unit/templates/conftest.py                                               |     2 |    0 |     2 |   100% |
| tests/unit/templates/test_base_metadata_extractor.py                           |    14 |   12 |     2 |    14% |
| tests/unit/templates/test_extraction_input_ae_payload.py                       |    12 |   11 |     1 |     8% |
| tests/unit/templates/test_extraction_input_filters.py                          |    22 |    8 |    14 |    64% |
| tests/unit/templates/test_incremental_sql_metadata_extractor.py                |    89 |   56 |    33 |    37% |
| tests/unit/templates/test_sql_app.py                                           |    54 |   34 |    20 |    37% |
| tests/unit/templates/test_sql_metadata_extractor.py                            |   131 |   99 |    32 |    24% |
| tests/unit/templates/test_sql_query_extractor.py                               |    15 |   12 |     3 |    20% |
| tests/unit/testing/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/testing/test_fixtures.py                                            |    16 |   15 |     1 |     6% |
| tests/unit/testing/test_mocks.py                                               |    29 |   28 |     1 |     3% |
| tests/unit/testing/test_parity.py                                              |    35 |   34 |     1 |     3% |
| tests/unit/testing/e2e/__init__.py                                             |     1 |    1 |     0 |     0% |
| tests/unit/testing/e2e/test_logs.py                                            |    10 |    8 |     2 |    20% |
| tests/unit/testing/e2e/test_portforward.py                                     |     7 |    1 |     6 |    86% |
| tests/unit/testing/full_dag/__init__.py                                        |     1 |    1 |     0 |     0% |
| tests/unit/testing/full_dag/test_base.py                                       |    22 |    3 |    19 |    86% |
| tests/unit/testing/full_dag/test_client.py                                     |    14 |   10 |     4 |    29% |
| tests/unit/testing/full_dag/test_payload.py                                    |    11 |    3 |     8 |    73% |
| tests/unit/testing/full_dag/test_sql_app.py                                    |    12 |    4 |     8 |    67% |
| tests/unit/testing/integration/__init__.py                                     |     1 |    1 |     0 |     0% |
| tests/unit/testing/integration/test_client.py                                  |    10 |    8 |     2 |    20% |
| tests/unit/testing/integration/test_comparison.py                              |    29 |    0 |    29 |   100% |
| tests/unit/testing/integration/test_lazy_pandas.py                             |     5 |    0 |     5 |   100% |
| tests/unit/testing/sdr/__init__.py                                             |     1 |    1 |     0 |     0% |
| tests/unit/testing/sdr/test_base.py                                            |    13 |   11 |     2 |    15% |
| tests/unit/tools/__init__.py                                                   |     1 |    1 |     0 |     0% |
| tests/unit/tools/test_check_migration.py                                       |    65 |   44 |    21 |    32% |
| tests/unit/tools/test_codemod_roundtrip.py                                     |    32 |   31 |     1 |     3% |
| tests/unit/tools/test_extract_context.py                                       |    50 |   48 |     2 |     4% |
| tests/unit/tools/test_fingerprint.py                                           |    23 |   21 |     2 |     9% |
| tests/unit/tools/test_provision_credentials.py                                 |    11 |    0 |    11 |   100% |
| tests/unit/tools/test_rewrite_imports.py                                       |    41 |   31 |    10 |    24% |
| tests/unit/tools/test_run_codemods.py                                          |    16 |   15 |     1 |     6% |
| tests/unit/tools/test_codemods/__init__.py                                     |     1 |    1 |     0 |     0% |
| tests/unit/tools/test_codemods/conftest.py                                     |     2 |    0 |     2 |   100% |
| tests/unit/tools/test_codemods/test_remove_activities_cls.py                   |    15 |   11 |     4 |    27% |
| tests/unit/tools/test_codemods/test_remove_decorators.py                       |    17 |   16 |     1 |     6% |
| tests/unit/tools/test_codemods/test_rewrite_activity_calls.py                  |    16 |   11 |     5 |    31% |
| tests/unit/tools/test_codemods/test_rewrite_entry_point.py                     |    19 |   16 |     3 |    16% |
| tests/unit/tools/test_codemods/test_rewrite_handlers.py                        |    13 |   12 |     1 |     8% |
| tests/unit/tools/test_codemods/test_rewrite_returns.py                         |    12 |   10 |     2 |    17% |
| tests/unit/tools/test_codemods/test_rewrite_signatures.py                      |    19 |   18 |     1 |     5% |
| tests/unit/transformers/__init__.py                                            |     1 |    1 |     0 |     0% |
| tests/unit/transformers/atlas/__init__.py                                      |     1 |    1 |     0 |     0% |
| tests/unit/transformers/atlas/test_atlas_transformer.py                        |    28 |   15 |    13 |    46% |
| tests/unit/transformers/atlas/test_column.py                                   |    17 |    6 |    11 |    65% |
| tests/unit/transformers/atlas/test_database.py                                 |     8 |    6 |     2 |    25% |
| tests/unit/transformers/atlas/test_function.py                                 |    11 |    5 |     6 |    55% |
| tests/unit/transformers/atlas/test_procedure.py                                |     7 |    6 |     1 |    14% |
| tests/unit/transformers/atlas/test_schema.py                                   |     8 |    6 |     2 |    25% |
| tests/unit/transformers/atlas/test_sql_extra.py                                |    42 |   39 |     3 |     7% |
| tests/unit/transformers/atlas/test_table.py                                    |    15 |    6 |     9 |    60% |
| tests/unit/transformers/common/__init__.py                                     |     1 |    1 |     0 |     0% |
| tests/unit/transformers/common/test_last_sync.py                               |    13 |    6 |     7 |    54% |
| tests/unit/transformers/query/test_sql_transformer.py                          |    16 |    4 |    12 |    75% |
| tests/unit/transformers/query/test_sql_transformer_output_validation.py        |     5 |    2 |     3 |    60% |
| tools/migrate_v3/__init__.py                                                   |     1 |    0 |     1 |   100% |
| tools/migrate_v3/check_migration.py                                            |    11 |    6 |     5 |    45% |
| tools/migrate_v3/contract_mapping.py                                           |     3 |    1 |     2 |    67% |
| tools/migrate_v3/extract_context.py                                            |    21 |   16 |     5 |    24% |
| tools/migrate_v3/fingerprint.py                                                |     3 |    1 |     2 |    67% |
| tools/migrate_v3/import_mapping.py                                             |     2 |    0 |     2 |   100% |
| tools/migrate_v3/rewrite_imports.py                                            |    19 |    8 |    11 |    58% |
| tools/migrate_v3/run_codemods.py                                               |    14 |    5 |     9 |    64% |
| tools/migrate_v3/codemods/__init__.py                                          |    11 |    4 |     7 |    64% |
| tools/migrate_v3/codemods/remove_activities_cls.py                             |    12 |    7 |     5 |    42% |
| tools/migrate_v3/codemods/remove_decorators.py                                 |    11 |    8 |     3 |    27% |
| tools/migrate_v3/codemods/rewrite_activity_calls.py                            |     6 |    1 |     5 |    83% |
| tools/migrate_v3/codemods/rewrite_entry_point.py                               |    10 |    3 |     7 |    70% |
| tools/migrate_v3/codemods/rewrite_handlers.py                                  |     8 |    5 |     3 |    38% |
| tools/migrate_v3/codemods/rewrite_returns.py                                   |     7 |    5 |     2 |    29% |
| tools/migrate_v3/codemods/rewrite_signatures.py                                |     8 |    4 |     4 |    50% |
|--------------------------------------------------------------------------------|-------|------|-------|--------|
| TOTAL                                                                          |  9274 | 4760 |  4514 |  48.7% |
---------------- RESULT: PASSED (minimum: 30.0%, actual: 48.7%) ----------------

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

📦 Trivy Vulnerability Scan Results

Schema Version Created At Artifact Type
2 2026-05-19T11:34:04.235843649Z . repository

Report Summary

Could not generate summary table (data length mismatch: 9 vs 8).

Scan Result Details

requirements.txt
uv.lock

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

📦 Trivy Secret Scan Results

Schema Version Created At Artifact Type
2 2026-05-19T11:34:19.354148203Z . repository

Report Summary

Could not generate summary table (data length mismatch: 9 vs 8).

Scan Result Details

requirements.txt
uv.lock

@atlan-ci
Copy link
Copy Markdown
Collaborator

atlan-ci commented May 18, 2026

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
17161 15358 89% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: e5662b6 by action🐍

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

🛠 Full Test Coverage Report: https://k.atlan.dev/coverage/application-sdk/pr/1774

@atlan-ci
Copy link
Copy Markdown
Collaborator

atlan-ci commented May 18, 2026

🧪 SDR Integration Tests (testcontainer) — mysql

Status: Passed ✅
Summary: ============================== 8 passed in 25.68s ==============================
Run: 26094476674

Temporal workflows:workflow_include_main_db · ✅ workflow_mixed_filters

Extracted + transformed assets

Workflow run 1mysql-0822058c0bd5691a-70d3ed16

Entity Extracted Transformed
column 11 11
database 1 1
extras-procedure 0
schema 1 1
table 3 3

Workflow run 2mysql-3b377dc2ca6be340-54a68a93

Entity Extracted Transformed
column 17 17
database 1 1
extras-procedure 0
schema 2 2
table 5 5

What this validates

  • Credential resolution from secret store — workflow username/password resolved via the Dapr local.file secret store using extraction_method=agent + agent_jsonAgentCredentialSpec, not passed inline. Validates the full SDR credential-ref → secret-store → connector-client chain.
  • Auth — valid credentials succeed; invalid credentials and unreachable host fail correctly
  • Preflight — connectivity, schema/table checks; bad credentials fail correctly
  • Workflow — full extraction run on the test instance, polled to COMPLETED on the tenant's Temporal cluster

Stack: atlan-configurator generated compose, Dapr sidecar embedded, Temporal worker on CI tenant.

Re-run

Push a new commit, or re-run from the Actions tab.

…tion

The pre-flight check in unit-tests-gate was using the default per_page=30 on
the /check-runs endpoint and a client-side jq filter. On commits with many
Dependabot or other bot-created check runs, the target unit-tests check can
fall to page 2+ and be invisible — causing the gate to abort with a spurious
"check missing" error even when the check exists and passed.

Switch to the API's server-side `check_name` query parameter, which filters
before paginating and exposes the count directly via .total_count.

Discovered while testing on atlan-clickhouse-app, where the SHA on main had
31 check-runs (15 of them Dependabot) and unit-tests sat at position 31.
…ck found

Adds an optional `unit_tests_workflow_file` input. When set, the gate's
pre-flight no longer fails on a missing check — instead it dispatches the
named workflow on the publish ref and falls through to the wait step, which
then picks up the newly-created check once GitHub registers the run.

Mode summary:
- Polling-only (default, when input unset): existing behaviour — gate fails
  fast if no check on SHA. Lower latency, requires the app's tests workflow
  to run on every publish commit independently (e.g. via push:main).
- Hybrid (when input set): freshness on demand. Requires the target
  workflow to declare workflow_dispatch:.

Limitation: gh workflow run accepts a branch or tag, never a bare SHA, so
the dispatched run executes on the branch's current HEAD. For typical
publish flows this matches the publish SHA exactly; edge cases (commit
moves between resolve and dispatch) accept millisecond-scale drift.
Make `unit_tests_workflow_file` required and remove `unit_tests_check_name`.
The gate now tracks tests by run ID end-to-end:
- Look for an existing run of the named workflow on the publish SHA.
- If none, dispatch on the publish ref and capture the new run's ID.
- Poll that run by ID until completion; require conclusion=success.

This removes the polling-only / hybrid mode split and the check-name
plumbing. Apps no longer need to expose a check called `unit-tests` — the
gate works regardless of how the test job is named. Tests workflow only
needs to declare `workflow_dispatch:` and exit non-zero on failure.

Replaces `fountainhead/action-wait-for-check` with an inline `gh run view`
poll loop, dropping a third-party SHA pin in the process.
@anurag-atlan anurag-atlan marked this pull request as ready for review May 19, 2026 06:57
Apps that haven't onboarded to the gate yet keep working — the gate
skips itself when the input is unset. Enforcement comes later via a
one-line follow-up that flips required: false → true.

Reasoning: the 72 consumer draft PRs are still draft (waiting for this
SDK PR to land). Making the input required would break every existing
caller the moment this merges, before consumer PRs can be merged in
sequence. This change lets the SDK PR land safely, then consumers
opt in at their pace.
anurag-atlan added a commit that referenced this pull request May 19, 2026
…-456 follow-up)

Flip the gate from opt-in (non-breaking) to mandatory. Every caller of
build-and-publish-app.yaml MUST now set unit_tests_workflow_file; callers
that don't are rejected by workflow_call at startup.

This is the enforcement-flip follow-up to #1774.
Sequence:
  1. #1774 lands the gate as opt-in (zero breakage to existing callers).
  2. The 72 consumer draft PRs are merged at each app team's pace, each
     setting unit_tests_workflow_file on its caller.
  3. THIS PR flips the input to required, forcing any laggard apps to
     onboard before they can publish again.

Do NOT merge this PR until consumer adoption is high enough. Any app
that has not merged its consumer PR by the time this lands will fail
to publish until they do.
While the gate is opt-in (unit_tests_workflow_file optional), make gate
failure a warning rather than a hard block. Lets app teams merge their
consumer PRs even when their current tests are broken — they get a
visible warning but their publishes continue working. Once the
enforcement-flip PR (#1788) lands, this reverts to exit 1 and gate
failure blocks publish properly.

Combined with workflow_file being optional, this gives a 2-step ramp:
  1. Today: opt-in + warn-only (no breakage possible)
  2. Enforcement flip: required + hard-block (laggards get forced)
@anurag-atlan anurag-atlan added the ready-to-merge All quality gates passed label May 19, 2026
@anurag-atlan anurag-atlan enabled auto-merge (squash) May 19, 2026 11:33
@anurag-atlan anurag-atlan merged commit 4d65832 into main May 19, 2026
39 checks passed
@anurag-atlan anurag-atlan deleted the anuragbadoni/distr-456-unit-tests-gate branch May 19, 2026 11:38
anurag-atlan added a commit that referenced this pull request May 19, 2026
After DISTR-456 (#1774) landed, build-and-publish on consumer apps that
haven't opted into the gate started skipping 'Publish to Marketplace'
even though all upstream jobs (prepare, build, merge, security-scan)
succeeded.

Root cause: GitHub Actions implicit skip-propagation. When unit-tests-gate
is skipped (input not configured), the skipped status cascades through
the needs chain to any downstream job whose if: lacks an explicit status
function. publish was the lone job with 'if: inputs.publish' — every
other downstream job already uses '!failure() && !cancelled() &&
needs.X.result == 'success'' which bypasses the implicit cascade.

Fix: bring publish's if: into the same shape as app-deployment-dispatcher
and the other gated jobs.

Refs DISTR-456
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge All quality gates passed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants