Skip to content

fix(preflight): reject empty connection_qualified_name before extraction [CONNECT-1738] - #3869

Open
SwarnadeepChatterjee wants to merge 2 commits into
mainfrom
swarnadeepchatterjee/connect-1738-preflight-cqn
Open

SwarnadeepChatterjee wants to merge 2 commits into
mainfrom
swarnadeepchatterjee/connect-1738-preflight-cqn

Conversation

@SwarnadeepChatterjee

@SwarnadeepChatterjee SwarnadeepChatterjee commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a platform-level connection qualified name check to the preflight gate activity
  • Catches the defect where the setup wizard submits a Connection snapshot with no qualifiedName and schedules the workflow anyway (CONNECT-1738)
  • The check runs before credential resolution (cheap, no secrets needed) and short-circuits with NOT_READY when the connection is present but the qualified name is empty
  • Respects the gate's enforce/soft mode — blocks in hard mode, reports would_block in soft mode
  • Works for all SDK-generated workflows (extractors, miners, etc.) — skips when no connection data exists

Context

A prior production incident traced repeated nightly failures to a setup wizard submitting a Connection snapshot carrying only defaultCredentialGuid and no qualifiedName. The existing ConnectionQualifiedNameEmptyError checks in application_sdk/common/incremental/helpers.py only fire during transform_data — after 12 extraction activities and ~5 minutes of wasted work. Neither the AE-level user_publish_preflight nor the app-level {app}:preflight validated the connection shape.

What changed

File Change
application_sdk/execution/_temporal/preflight_gate.py Added _check_connection_qualified_name() helper + call in gate activity before credential resolution
tests/unit/execution/test_preflight_gate_connection_qn.py 11 unit tests covering pass/fail/skip scenarios

Test plan

  • 11 new unit tests pass (empty QN, missing QN, whitespace, valid QN, no connection data, snake_case attr, None attrs)
  • 335 existing preflight gate tests pass (no regressions)
  • Pre-commit (ruff, ruff-format, pyright, isort) passes

…ion [CONNECT-1738]

The setup wizard can submit a Connection snapshot whose attributes map
has no qualifiedName and schedule the workflow anyway. Nothing in the
frontend, heracles, or the SDK preflight gate rejects it. The run then
fails deep in transform_data after minutes of wasted extraction work.

Add a platform-level check to the preflight gate activity that validates
the connection qualified name from the extraction snapshot before
credential resolution. The check:

- Inspects both connection.attributes.qualifiedName and the top-level
  connection_qualified_name field
- Skips when no connection data exists (non-connection workflows)
- Returns NOT_READY with a typed InvalidInputError when the connection
  is present but the qualified name is empty
- Respects the gate's enforce/soft mode (blocks in hard, reports in soft)
@linear

linear Bot commented Sep 20, 2026

Copy link
Copy Markdown

CONNECT-1738

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

📜 Docstring Coverage Report

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

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                                                                   |     6 |     2 |     4 |    67% |
| application_sdk/discovery.py                                                                   |    12 |     3 |     9 |    75% |
| application_sdk/main.py                                                                        |    42 |    10 |    32 |    76% |
| application_sdk/main_errors.py                                                                 |     5 |     0 |     5 |   100% |
| application_sdk/pkl_version.py                                                                 |     1 |     0 |     1 |   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/_artifact_schema_guard.py                                                  |     8 |     0 |     8 |   100% |
| application_sdk/app/_ep_registration.py                                                        |     6 |     0 |     6 |   100% |
| application_sdk/app/_generated_tree.py                                                         |     8 |     0 |     8 |   100% |
| application_sdk/app/base.py                                                                    |    87 |    20 |    67 |    77% |
| application_sdk/app/base_errors.py                                                             |     7 |     0 |     7 |   100% |
| application_sdk/app/build_identity.py                                                          |     2 |     0 |     2 |   100% |
| application_sdk/app/client.py                                                                  |     1 |     0 |     1 |   100% |
| application_sdk/app/context.py                                                                 |    41 |     2 |    39 |    95% |
| application_sdk/app/entrypoint.py                                                              |    21 |     5 |    16 |    76% |
| 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/asset_serialization.py                                                  |    17 |     4 |    13 |    76% |
| application_sdk/common/atomic.py                                                               |    16 |     0 |    16 |   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/dispatch.py                                                             |     3 |     0 |     3 |   100% |
| application_sdk/common/entity_envelope.py                                                      |    10 |     0 |    10 |   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                                                               |     8 |     0 |     8 |   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/last_sync.py                                                            |     6 |     0 |     6 |   100% |
| 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                                                 |     3 |     0 |     3 |   100% |
| 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                                                           |    12 |     2 |    10 |    83% |
| application_sdk/contracts/types.py                                                             |    22 |     0 |    22 |   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/dev/_pkl_errors.py                                                             |     3 |     0 |     3 |   100% |
| application_sdk/dev/pkl.py                                                                     |    16 |     6 |    10 |    62% |
| application_sdk/errors/__init__.py                                                             |     4 |     1 |     3 |    75% |
| application_sdk/errors/base.py                                                                 |    11 |     2 |     9 |    82% |
| application_sdk/errors/categories.py                                                           |     3 |     0 |     3 |   100% |
| application_sdk/errors/leaves.py                                                               |    23 |     8 |    15 |    65% |
| 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                                                         |    21 |     3 |    18 |    86% |
| 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                                         |     6 |     4 |     2 |    33% |
| application_sdk/execution/_temporal/_lock_errors.py                                            |     5 |     0 |     5 |   100% |
| application_sdk/execution/_temporal/activities.py                                              |    12 |     0 |    12 |   100% |
| application_sdk/execution/_temporal/activity_utils.py                                          |     6 |     0 |     6 |   100% |
| application_sdk/execution/_temporal/auth.py                                                    |    16 |     0 |    16 |   100% |
| application_sdk/execution/_temporal/backend.py                                                 |    16 |     1 |    15 |    94% |
| application_sdk/execution/_temporal/converter.py                                               |     3 |     0 |     3 |   100% |
| application_sdk/execution/_temporal/eviction_retry.py                                          |     4 |     0 |     4 |   100% |
| application_sdk/execution/_temporal/lock_activities.py                                         |     3 |     0 |     3 |   100% |
| application_sdk/execution/_temporal/poll_state.py                                              |     7 |     1 |     6 |    86% |
| application_sdk/execution/_temporal/preflight_gate.py                                          |    78 |     6 |    72 |    92% |
| application_sdk/execution/_temporal/preflight_persist.py                                       |    14 |     0 |    14 |   100% |
| application_sdk/execution/_temporal/sdr.py                                                     |    18 |     8 |    10 |    56% |
| application_sdk/execution/_temporal/worker.py                                                  |    17 |     6 |    11 |    65% |
| application_sdk/execution/_temporal/workflows.py                                               |     3 |     0 |     3 |   100% |
| application_sdk/execution/_temporal/interceptors/__init__.py                                   |     1 |     0 |     1 |   100% |
| application_sdk/execution/_temporal/interceptors/events.py                                     |    21 |     3 |    18 |    86% |
| 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                                                           |    45 |     8 |    37 |    82% |
| application_sdk/handler/manifest.py                                                            |     5 |     0 |     5 |   100% |
| application_sdk/handler/service.py                                                             |    67 |    23 |    44 |    66% |
| 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                                                      |     8 |     0 |     8 |   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                                                 |    32 |     4 |    28 |    88% |
| 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                                                        |    20 |     1 |    19 |    95% |
| 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                                            |    17 |     4 |    13 |    76% |
| application_sdk/observability/events.py                                                        |     1 |     0 |     1 |   100% |
| application_sdk/observability/logger_adaptor.py                                                |    56 |    10 |    46 |    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                                                 |    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                                                        |    16 |     7 |     9 |    56% |
| 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                                                         |     8 |     1 |     7 |    88% |
| 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/_locks.py                                                              |     5 |     1 |     4 |    80% |
| 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                                                             |    12 |     0 |    12 |   100% |
| application_sdk/storage/cloud.py                                                               |    24 |     6 |    18 |    75% |
| application_sdk/storage/errors.py                                                              |    49 |    32 |    17 |    35% |
| application_sdk/storage/factory.py                                                             |     3 |     0 |     3 |   100% |
| application_sdk/storage/file_ref_sync.py                                                       |    15 |     3 |    12 |    80% |
| application_sdk/storage/integrity.py                                                           |    14 |     0 |    14 |   100% |
| application_sdk/storage/ops.py                                                                 |    33 |     1 |    32 |    97% |
| application_sdk/storage/preflight.py                                                           |    13 |     0 |    13 |   100% |
| application_sdk/storage/reference.py                                                           |    11 |     1 |    10 |    91% |
| 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                                                           |    50 |     6 |    44 |    88% |
| application_sdk/templates/sql_app_errors.py                                                    |     9 |     0 |     9 |   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                                                            |     2 |     0 |     2 |   100% |
| application_sdk/testing/_errors.py                                                             |     4 |     0 |     4 |   100% |
| application_sdk/testing/_mustache.py                                                           |     2 |     0 |     2 |   100% |
| application_sdk/testing/fake_source.py                                                         |    59 |    16 |    43 |    73% |
| application_sdk/testing/fixtures.py                                                            |    11 |     0 |    11 |   100% |
| application_sdk/testing/golden.py                                                              |    26 |    10 |    16 |    62% |
| application_sdk/testing/mocks.py                                                               |    68 |    17 |    51 |    75% |
| application_sdk/testing/preflight.py                                                           |    20 |     6 |    14 |    70% |
| application_sdk/testing/setup_routes.py                                                        |    45 |     5 |    40 |    89% |
| application_sdk/testing/volatile_fields.py                                                     |     1 |     0 |     1 |   100% |
| application_sdk/testing/e2e/__init__.py                                                        |     1 |     0 |     1 |   100% |
| application_sdk/testing/e2e/_errors.py                                                         |    13 |     0 |    13 |   100% |
| application_sdk/testing/e2e/_manifest_identity.py                                              |    11 |     0 |    11 |   100% |
| application_sdk/testing/e2e/base.py                                                            |   128 |     7 |   121 |    95% |
| application_sdk/testing/e2e/client.py                                                          |    29 |     7 |    22 |    76% |
| application_sdk/testing/e2e/config.py                                                          |     5 |     0 |     5 |   100% |
| application_sdk/testing/e2e/credential.py                                                      |     2 |     0 |     2 |   100% |
| application_sdk/testing/e2e/logs.py                                                            |     9 |     1 |     8 |    89% |
| application_sdk/testing/e2e/payload.py                                                         |    11 |     0 |    11 |   100% |
| application_sdk/testing/e2e/portforward.py                                                     |     1 |     0 |     1 |   100% |
| application_sdk/testing/e2e/sql_app.py                                                         |    10 |     0 |    10 |   100% |
| application_sdk/testing/e2e/substitutions.py                                                   |     3 |     0 |     3 |   100% |
| application_sdk/testing/e2e/workflows.py                                                       |     6 |     1 |     5 |    83% |
| 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                                                       |    18 |     2 |    16 |    89% |
| 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                                                    |     6 |     1 |     5 |    83% |
| application_sdk/testing/harness/__init__.py                                                    |     1 |     0 |     1 |   100% |
| application_sdk/testing/harness/_errors.py                                                     |    13 |     0 |    13 |   100% |
| application_sdk/testing/harness/_poll.py                                                       |    15 |     0 |    15 |   100% |
| application_sdk/testing/harness/bridge.py                                                      |     5 |     0 |     5 |   100% |
| application_sdk/testing/harness/budgets.py                                                     |     6 |     0 |     6 |   100% |
| application_sdk/testing/harness/evidence.py                                                    |    15 |     1 |    14 |    93% |
| application_sdk/testing/harness/expectations.py                                                |    37 |     0 |    37 |   100% |
| application_sdk/testing/harness/fixtures.py                                                    |    43 |     4 |    39 |    91% |
| application_sdk/testing/harness/identity.py                                                    |    11 |     1 |    10 |    91% |
| application_sdk/testing/harness/outcome.py                                                     |    15 |     0 |    15 |   100% |
| application_sdk/testing/harness/preconditions.py                                               |    19 |     4 |    15 |    79% |
| application_sdk/testing/harness/spec.py                                                        |     2 |     0 |     2 |   100% |
| application_sdk/testing/harness/substrate.py                                                   |     3 |     0 |     3 |   100% |
| application_sdk/testing/harness/waiting.py                                                     |     8 |     0 |     8 |   100% |
| application_sdk/testing/harness/atlas/__init__.py                                              |    27 |     6 |    21 |    78% |
| application_sdk/testing/harness/atlas/_errors.py                                               |     2 |     0 |     2 |   100% |
| application_sdk/testing/harness/automation_engine/__init__.py                                  |     1 |     0 |     1 |   100% |
| application_sdk/testing/harness/automation_engine/_errors.py                                   |    10 |     0 |    10 |   100% |
| application_sdk/testing/harness/automation_engine/client.py                                    |    37 |     4 |    33 |    89% |
| application_sdk/testing/harness/automation_engine/retry.py                                     |    17 |     1 |    16 |    94% |
| application_sdk/testing/harness/automation_engine/wire.py                                      |    24 |     2 |    22 |    92% |
| application_sdk/testing/harness/cluster/__init__.py                                            |     1 |     0 |     1 |   100% |
| application_sdk/testing/harness/cluster/_errors.py                                             |     4 |     0 |     4 |   100% |
| application_sdk/testing/harness/cluster/_portforward.py                                        |    15 |     2 |    13 |    87% |
| application_sdk/testing/harness/cluster/_protocols.py                                          |     9 |     0 |     9 |   100% |
| application_sdk/testing/harness/cluster/_states.py                                             |     9 |     0 |     9 |   100% |
| application_sdk/testing/harness/cluster/kube.py                                                |    50 |     7 |    43 |    86% |
| application_sdk/testing/harness/seed/__init__.py                                               |     5 |     0 |     5 |   100% |
| application_sdk/testing/harness/seed/_errors.py                                                |     7 |     0 |     7 |   100% |
| application_sdk/testing/harness/seed/_ndjson.py                                                |     7 |     0 |     7 |   100% |
| application_sdk/testing/harness/seed/_publish.py                                               |     8 |     0 |     8 |   100% |
| application_sdk/testing/harness/seed/_spec.py                                                  |    11 |     0 |    11 |   100% |
| application_sdk/testing/harness/starters/__init__.py                                           |     2 |     0 |     2 |   100% |
| application_sdk/testing/harness/starters/_ae.py                                                |     4 |     0 |     4 |   100% |
| application_sdk/testing/harness/starters/_errors.py                                            |     4 |     0 |     4 |   100% |
| application_sdk/testing/harness/starters/_queue.py                                             |     3 |     0 |     3 |   100% |
| application_sdk/testing/harness/starters/_specs.py                                             |    12 |     0 |    12 |   100% |
| application_sdk/testing/harness/teardown/__init__.py                                           |     5 |     0 |     5 |   100% |
| application_sdk/testing/harness/teardown/_dag.py                                               |     4 |     0 |     4 |   100% |
| application_sdk/testing/harness/teardown/_purge.py                                             |     8 |     0 |     8 |   100% |
| application_sdk/testing/harness/temporal/__init__.py                                           |     1 |     0 |     1 |   100% |
| application_sdk/testing/harness/temporal/_errors.py                                            |     5 |     0 |     5 |   100% |
| application_sdk/testing/harness/temporal/_protocols.py                                         |     4 |     0 |     4 |   100% |
| application_sdk/testing/harness/temporal/_states.py                                            |     6 |     0 |     6 |   100% |
| application_sdk/testing/harness/temporal/client.py                                             |    23 |     1 |    22 |    96% |
| 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                                                |     3 |     1 |     2 |    67% |
| application_sdk/testing/integration/_errors.py                                                 |    17 |     0 |    17 |   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/corpus.py                                                  |    25 |     7 |    18 |    72% |
| application_sdk/testing/integration/fixtures.py                                                |    26 |     1 |    25 |    96% |
| 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                                                  |    27 |     2 |    25 |    93% |
| 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                                               |     1 |     0 |     1 |   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/artifacts.py                                                        |    28 |     2 |    26 |    93% |
| application_sdk/validation/assets.py                                                           |    23 |     2 |    21 |    91% |
| application_sdk/validation/interceptor.py                                                      |    17 |     0 |    17 |   100% |
| application_sdk/validation/ndjson.py                                                           |    27 |     4 |    23 |    85% |
| application_sdk/validation/parquet.py                                                          |    16 |     0 |    16 |   100% |
| application_sdk/validation/protocols.py                                                        |     9 |     0 |     9 |   100% |
| application_sdk/validation/sources.py                                                          |    15 |     0 |    15 |   100% |
| application_sdk/validation/wrapper.py                                                          |     8 |     0 |     8 |   100% |
| 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/artifact-schemas/app/generated/__init__.py                           |     1 |     1 |     0 |     0% |
| contract-toolkit/examples/artifact-schemas/app/generated/_e2e_base.py                          |     2 |     2 |     0 |     0% |
| contract-toolkit/examples/artifact-schemas/app/generated/_e2e_substitutions.py                 |     2 |     2 |     0 |     0% |
| contract-toolkit/examples/artifact-schemas/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/preflight_testing.py                                          |    12 |     7 |     5 |    42% |
| 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                                       |    12 |     0 |    12 |   100% |
| packages/conformance/conformance/bootstrap/command.py                                          |    20 |     2 |    18 |    90% |
| packages/conformance/conformance/bootstrap/extract.py                                          |    30 |     0 |    30 |   100% |
| packages/conformance/conformance/bootstrap/render.py                                           |     3 |     0 |     3 |   100% |
|

This message was truncated. Download full message

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

📦 Trivy Vulnerability Scan Results

Schema Version Created At Artifact Type
2 2026-09-20T10:05:32.75757893Z . repository

Report Summary

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

Scan Result Details

packages/conformance/uv.lock
uv.lock

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

📦 Trivy Secret Scan Results

Schema Version Created At Artifact Type
2 2026-09-20T10:05:41.625288104Z . repository

Report Summary

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

Scan Result Details

packages/conformance/uv.lock
uv.lock

@atlan-app-fleet

atlan-app-fleet Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

☂️ Code Coverage

current status: ✅

Overall Coverage

Statements Covered Coverage Threshold Status
30761 28897 94% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: 9c78b55 by action🐍

…havioral tests

Re-apply the _check_connection_qualified_name helper and gate insertion
that were lost during a stash-pop merge conflict. The check now runs
inside the heartbeat try-block (after beats is initialised) so
_emit_outcome can access the liveness guard.

Replace the import-only red tests with proper behavioral gate-activity
tests that exercise build_preflight_gate_activity end-to-end:
- hard mode raises PreflightFailed on the broken snapshot
- soft mode returns NOT_READY without raising
- healthy snapshots proceed normally
- non-connection snapshots skip the check
- handler is never invoked when CQN is empty (short-circuit)

Ref: CONNECT-1738

@cmgrote cmgrote left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for chasing this down to a concrete seam — the failure mode is real and the "reject before the 12 extraction activities" instinct is the right one. My concern is with where the rejection lives, and I think the gate placement is load-bearing for three defects rather than just a style preference.

The invariant already belongs to the contract

ConnectionAttributes already states this rule in its own docstring (application_sdk/contracts/types.py:504):

qualified_name and name are identity: a Connection without them is not addressable, so an explicit null is rejected rather than coerced.

The model enforces that for an explicit null and not for an absent key — absent falls through to qualified_name: str = "". CONNECT-1738 is precisely the absent case. So this isn't a missing check; it's a hole in an invariant the contract already claims to own, and the PR patches the symptom two layers downstream of it.

Doing it on ConnectionRef buys four things the gate can't:

contract gate check
coverage every reader — publish node, incremental helpers, e2e harness, preflight_persist only workflows with the gate injected
policy invariant, always behind App.preflight_gate_mode, default "soft"
aliasing alias_generator=to_camel + populate_by_name already resolve camel/snake hand-rolled .get("qualifiedName") or .get("qualified_name")
readers one two, which already disagree (#3 below)

The honest counter-argument: a model_validator that raises during workflow-input deserialization becomes a workflow-task failure that retries forever with an opaque pydantic error — worse than today's five-minute failure. So I'd split it: the contract owns the predicate, an activity owns the verdict. Add something like ConnectionRef.is_addressable (or a classifier returning "absent / addressable / populated-but-unidentifiable"), and have the gate call that instead of re-deriving it from a raw dict. One definition, still a clean terminal failure.

Separately, if we want the submit path to reject too: /workflows/v1/start validates via a bare model_validate (handler/service.py:1273) rather than _validate_request, so a contract-level raise there answers 500 instead of a 422 naming the field. Small fix, worth pairing with this.

Three defects I reproduced on the branch

1. The soft default means this doesn't fix the reported failure

App.preflight_gate_mode defaults to "soft" (app/base.py:820), and every block site in preflight_gate.py is behind if enforce (2224, 2279, 2444). There's no unconditional-block path. Unless the app has opted into hard mode, the run still executes all 12 extraction activities — the original symptom is unchanged.

test_soft_mode_returns_not_ready_for_empty_cqn asserts exactly this behaviour and counts it as a pass, which is what hides it.

Worth deciding explicitly: is "the payload is malformed" a source-readiness opinion the app gets to soften? I'd argue no, and that this outcome should block irrespective of mode — which is another reason it doesn't sit naturally inside the gate's verdict taxonomy.

2. "Skips when no connection data exists" doesn't hold — it blocks every generated app

ExtractionInput.connection is Field(default_factory=ConnectionRef), and a default ConnectionRef dumps to {'typeName': 'Connection', 'attributes': {}} — never None. So if top_level_cqn is None and connection is None is dead code for every SDK-generated app:

from application_sdk.execution._temporal.preflight_gate import _check_connection_qualified_name
from application_sdk.templates.contracts.sql_metadata import ExtractionInput

snapshot = ExtractionInput().model_dump(mode="json")
print(snapshot["connection"])
# {'typeName': 'Connection', 'attributes': {}}

print(_check_connection_qualified_name(snapshot))
# name='connection_qualified_name' passed=False message='Connection snapshot is
# missing qualifiedName. ...'   ← expected None (no connection was ever selected)

Every toolkit example subclasses that ExtractionInput, including minimal, which declares no connection widget at all. Soft mode turns that into a would_block row on every healthy run of every connectionless app; hard mode blocks them.

3. It's a second reader of a value that already has a canonical one

preflight_persist.connection_qualified_name() (preflight_persist.py:241) already reads this, through ConnectionRef, and additionally handles the connection-qualified-name kebab key and the single-element-list form. Same activity, same snapshot, opposite answers:

from application_sdk.execution._temporal.preflight_gate import _check_connection_qualified_name as new
from application_sdk.execution._temporal.preflight_persist import connection_qualified_name as canonical

cases = {
    "list-of-one": {"connection_qualified_name": ["default/x/1700000000"]},
    "kebab key":   {"connection-qualified-name": "default/x/1700000000",
                    "connection": {"attributes": {}}},
}
for label, snap in cases.items():
    verdict = "BLOCK" if new(snap) is not None else "pass"
    print(f"{label:12} canonical={canonical(snap)!r:28} new_check={verdict}")

# list-of-one  canonical='default/x/1700000000'  new_check=BLOCK
# kebab key    canonical='default/x/1700000000'  new_check=BLOCK

The same activity would persist a row carrying the qualified name alongside a verdict saying it is empty.

Common root cause for 2 and 3: every fixture in the new test file is a hand-written dict, so nothing exercises the shape a real ExtractionInput.model_dump(mode="json") actually produces. Building the fixtures from a real input would have caught both.

Suggested shape

  1. Put the predicate on ConnectionRef / ConnectionAttributes — "populated but carries no identity".
  2. Have the gate read the qualified name via preflight_persist.connection_qualified_name() and classify with that predicate; drop _check_connection_qualified_name entirely.
  3. Decide whether a malformed-payload outcome blocks regardless of preflight_gate_mode, and say so in the code either way.
  4. Rebuild the fixtures from real ExtractionInput(...).model_dump(mode="json") payloads, and add a case for an app with no connection widget.

This branch has not been deployed

No deployments
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.

2 participants