Skip to content

fix(handler): filesystem-resolve bundle marketplace entrypoints for input-contract and bare-manifest - #3090

Merged
cmgrote merged 11 commits into
mainfrom
aryaman/bundle-entrypoint-registry
Aug 10, 2026
Merged

fix(handler): filesystem-resolve bundle marketplace entrypoints for input-contract and bare-manifest#3090
cmgrote merged 11 commits into
mainfrom
aryaman/bundle-entrypoint-registry

Conversation

@Aryamanz29

@Aryamanz29 Aryamanz29 commented Aug 10, 2026

Copy link
Copy Markdown
Member

Problem

Bundle ("uber") apps expose marketplace entry points only as generated contract dirs (app/generated/<ep>/manifest.json) whose DAGs are computed per submission via compute_manifest. Their @entrypoint registrations are the DAG-node workflows (<ep>-post, <ep>-build-raw-sql, …) — never the marketplace name. Registry-driven resolution therefore fails on live bundles (reproduced on a tenant against the deployed app):

Route Result today
GET /workflows/v1/manifest?entrypoint=<ep> 200 (filesystem glob) ✅
GET /workflows/v1/input-contract?entrypoint=<ep> 404 "No input contract for entrypoint"
bare GET /workflows/v1/input-contract 200 but serves a DAG-node sibling's schema ⚠️

The input-contract 404 breaks Heracles' POST /v1/app creation for every bundle entry point (Sev1 RCA): the create flow fetches the input contract with the entrypoint → 1003 → the eventual manifest fetch dies with "No manifest available".

Fix (scoped per review)

input-contract only: on a registry miss for a named entrypoint whose generated dir exists, serve AppInputContract from app.generated.<ep_snake>._input (in-place) or app.<ep_snake>._input (the bundle regen relocation path — kebab generated dirs aren't importable, so regen scripts move _input.py into the entrypoint package). Entry-point names are regex-validated and containment-proven under app/generated/ before touching disk.

Registry hits keep winning; apps without generated dirs are byte-for-byte unchanged — the fallback can only turn this specific 404 into a 200.

Per @cmgrote's review, the earlier bare-manifest disk-glob fallback was dropped (serving one of N marketplace manifests picked alphabetically is confidently wrong; the honest answer is the existing 404) — a regression test now pins that behavior. The durable design — toolkit-emitted app/generated/entrypoints.json + static JSON schemas, one declarative resolver, delete the #2764/#2776 fallbacks and this one — is FND-180.

Tests

4 tests in TestBundleMarketplaceEntrypoints (disk-resolved contract, both 404-preservation cases, bare-manifest-stays-404). Full unit suite green.

Validation

Deployed to a test tenant via a companion app build: all three bundle entry points' input contracts now serve 200 (were 404), unknown entrypoints still 404, manifest route unchanged — see the results table on atlanhq/atlan-csa-uber-app#171.

Merge train (this PR is the head)

  1. This PR → next SDK release. Blocks everything below.
  2. atlanhq/atlan-csa-uber-app#171 — adds the AppInputContract classes this route serves; currently branch-pinned to this PR, swaps to the released SDK before merge.
  3. feat: v3 app builder for Asset Export (Basic) + migration pointers on all legacy packages (AICHAT-1588) atlan-python#1004 — the pyatlan v3 builder for asset-export-basic; its /v1/app path works once (2) is deployed on a tenant.

…nput-contract and bare-manifest

Bundle ("uber") apps expose marketplace entry points only as generated
contract dirs (app/generated/<ep>/manifest.json) whose DAGs are computed
per submission; their @entrypoint registrations are the DAG-node
workflows (<ep>-post, <ep>-build-raw-sql, ...), never the marketplace
name. Registry-driven resolution therefore 404s:

- GET /workflows/v1/input-contract?entrypoint=<ep> -> 404 "No input
  contract for entrypoint", which breaks Heracles' /v1/app creation for
  every bundle entry point (a production RCA: the caller's
  retry-without-entrypoint then validated against a sibling DAG-node's
  schema and died on the bare manifest call)
- bare GET /workflows/v1/manifest -> 404 "No manifest available" (the
  registry-derived candidates are DAG-node workflows with no manifest
  dirs), breaking submit-time pre-validation for bundles

The manifest and configmap routes are already filesystem-first for
exactly this layout; this gives the remaining two surfaces the same
authority:

- input-contract: on a registry miss for a named entrypoint whose
  generated dir exists, serve AppInputContract from
  app.generated.<ep_snake>._input (in-place) or app.<ep_snake>._input
  (the bundle regen relocation path — kebab generated dirs are not
  importable, so regen moves _input.py into the entrypoint package)
- bare manifest: append the on-disk entry points (alphabetical) to the
  fallback candidates so a bundle serves deterministically

Registry hits keep winning; apps without generated dirs are unchanged.
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📜 Docstring Coverage Report

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

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                                   |    28 |    2 |    26 |    93% |
| application_sdk/__init__.py                                                                  |     1 |    0 |     1 |   100% |
| application_sdk/_discovery_errors.py                                                         |     7 |    0 |     7 |   100% |
| application_sdk/constants.py                                                                 |     5 |    2 |     3 |    60% |
| application_sdk/discovery.py                                                                 |    12 |    3 |     9 |    75% |
| application_sdk/main.py                                                                      |    35 |    8 |    27 |    77% |
| 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/_ep_registration.py                                                      |     6 |    0 |     6 |   100% |
| application_sdk/app/base.py                                                                  |    79 |   19 |    60 |    76% |
| application_sdk/app/base_errors.py                                                           |     5 |    0 |     5 |   100% |
| application_sdk/app/client.py                                                                |     1 |    0 |     1 |   100% |
| application_sdk/app/context.py                                                               |    39 |    2 |    37 |    95% |
| application_sdk/app/entrypoint.py                                                            |    15 |    4 |    11 |    73% |
| application_sdk/app/registry.py                                                              |    40 |   11 |    29 |    72% |
| 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/_env.py                                                               |     2 |    0 |     2 |   100% |
| application_sdk/common/_listing.py                                                           |     4 |    0 |     4 |   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/filter_matching.py                                                    |     9 |    3 |     6 |    67% |
| application_sdk/common/models.py                                                             |     4 |    2 |     2 |    50% |
| application_sdk/common/path.py                                                               |     2 |    1 |     1 |    50% |
| application_sdk/common/spillable_dict.py                                                     |    17 |   11 |     6 |    35% |
| application_sdk/common/sql_filters.py                                                        |    14 |    2 |    12 |    86% |
| application_sdk/common/sql_filters_errors.py                                                 |     2 |    0 |     2 |   100% |
| application_sdk/common/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/compat.py                                                          |     9 |    1 |     8 |    89% |
| application_sdk/contracts/events.py                                                          |    12 |    0 |    12 |   100% |
| application_sdk/contracts/storage.py                                                         |     6 |    1 |     5 |    83% |
| application_sdk/contracts/types.py                                                           |    15 |    0 |    15 |   100% |
| application_sdk/contracts/types_errors.py                                                    |     2 |    0 |     2 |   100% |
| application_sdk/credentials/__init__.py                                                      |     1 |    0 |     1 |   100% |
| application_sdk/credentials/agent.py                                                         |    13 |    3 |    10 |    77% |
| application_sdk/credentials/atlan.py                                                         |    12 |    6 |     6 |    50% |
| application_sdk/credentials/atlan_client.py                                                  |     6 |    0 |     6 |   100% |
| application_sdk/credentials/errors.py                                                        |    20 |   12 |     8 |    40% |
| application_sdk/credentials/git.py                                                           |     9 |    6 |     3 |    33% |
| application_sdk/credentials/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                                                         |     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                                                          |     7 |    6 |     1 |    14% |
| application_sdk/dev/_embedded.py                                                             |     3 |    0 |     3 |   100% |
| application_sdk/errors/__init__.py                                                           |     4 |    1 |     3 |    75% |
| application_sdk/errors/base.py                                                               |    10 |    2 |     8 |    80% |
| application_sdk/errors/categories.py                                                         |     3 |    0 |     3 |   100% |
| application_sdk/errors/leaves.py                                                             |    17 |    8 |     9 |    53% |
| 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                                                       |    21 |    3 |    18 |    86% |
| application_sdk/execution/retry.py                                                           |     7 |    0 |     7 |   100% |
| application_sdk/execution/sandbox.py                                                         |     4 |    0 |     4 |   100% |
| application_sdk/execution/settings.py                                                        |     7 |    1 |     6 |    86% |
| application_sdk/execution/shutdown.py                                                        |     4 |    0 |     4 |   100% |
| application_sdk/execution/_temporal/__init__.py                                              |     1 |    1 |     0 |     0% |
| application_sdk/execution/_temporal/_activity_errors.py                                      |     8 |    0 |     8 |   100% |
| application_sdk/execution/_temporal/_backend_errors.py                                       |     5 |    4 |     1 |    20% |
| application_sdk/execution/_temporal/_lock_errors.py                                          |     5 |    0 |     5 |   100% |
| application_sdk/execution/_temporal/activities.py                                            |     8 |    0 |     8 |   100% |
| application_sdk/execution/_temporal/activity_utils.py                                        |     6 |    0 |     6 |   100% |
| application_sdk/execution/_temporal/auth.py                                                  |    13 |    0 |    13 |   100% |
| application_sdk/execution/_temporal/backend.py                                               |    15 |    1 |    14 |    93% |
| application_sdk/execution/_temporal/converter.py                                             |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/eviction_retry.py                                        |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/lock_activities.py                                       |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/preflight_gate.py                                        |    32 |    4 |    28 |    88% |
| application_sdk/execution/_temporal/sdr.py                                                   |    16 |    7 |     9 |    56% |
| application_sdk/execution/_temporal/worker.py                                                |    11 |    5 |     6 |    55% |
| application_sdk/execution/_temporal/workflows.py                                             |     2 |    0 |     2 |   100% |
| application_sdk/execution/_temporal/interceptors/__init__.py                                 |     1 |    0 |     1 |   100% |
| application_sdk/execution/_temporal/interceptors/events.py                                   |    13 |    0 |    13 |   100% |
| application_sdk/execution/_temporal/interceptors/liveness.py                                 |    11 |    9 |     2 |    18% |
| application_sdk/execution/_temporal/interceptors/lock.py                                     |    10 |    2 |     8 |    80% |
| application_sdk/execution/_temporal/interceptors/log.py                                      |    22 |   12 |    10 |    45% |
| application_sdk/execution/_temporal/interceptors/metrics.py                                  |    18 |   15 |     3 |    17% |
| application_sdk/execution/_temporal/interceptors/outputs.py                                  |     9 |    0 |     9 |   100% |
| application_sdk/execution/_temporal/interceptors/trace.py                                    |     6 |    4 |     2 |    33% |
| application_sdk/handler/__init__.py                                                          |     1 |    0 |     1 |   100% |
| application_sdk/handler/base.py                                                              |    14 |    3 |    11 |    79% |
| application_sdk/handler/context.py                                                           |    18 |    5 |    13 |    72% |
| application_sdk/handler/contracts.py                                                         |    38 |    5 |    33 |    87% |
| application_sdk/handler/manifest.py                                                          |     5 |    0 |     5 |   100% |
| application_sdk/handler/service.py                                                           |    63 |   22 |    41 |    65% |
| application_sdk/handler/service_errors.py                                                    |     4 |    0 |     4 |   100% |
| application_sdk/infrastructure/__init__.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_secret_utils.py                                              |     2 |    0 |     2 |   100% |
| application_sdk/infrastructure/bindings.py                                                   |    16 |    3 |    13 |    81% |
| application_sdk/infrastructure/capacity.py                                                   |    11 |    0 |    11 |   100% |
| application_sdk/infrastructure/context.py                                                    |     6 |    0 |     6 |   100% |
| application_sdk/infrastructure/credential_vault.py                                           |     7 |    3 |     4 |    57% |
| application_sdk/infrastructure/pubsub.py                                                     |    13 |    3 |    10 |    77% |
| application_sdk/infrastructure/secrets.py                                                    |    23 |    7 |    16 |    70% |
| 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                                         |     2 |    0 |     2 |   100% |
| application_sdk/infrastructure/_dapr/client.py                                               |    31 |    4 |    27 |    87% |
| application_sdk/infrastructure/_dapr/credential_vault.py                                     |    18 |    7 |    11 |    61% |
| application_sdk/infrastructure/_dapr/http.py                                                 |    21 |   14 |     7 |    33% |
| 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                                                     |     6 |    0 |     6 |   100% |
| application_sdk/observability/correlation.py                                                 |     6 |    0 |     6 |   100% |
| application_sdk/observability/dapr_log_forwarder.py                                          |    14 |    4 |    10 |    71% |
| application_sdk/observability/logger_adaptor.py                                              |    55 |    9 |    46 |    84% |
| application_sdk/observability/logger_adaptor_errors.py                                       |     2 |    0 |     2 |   100% |
| application_sdk/observability/metrics.py                                                     |     8 |    6 |     2 |    25% |
| application_sdk/observability/metrics_adaptor.py                                             |    13 |    2 |    11 |    85% |
| application_sdk/observability/models.py                                                      |     6 |    0 |     6 |   100% |
| application_sdk/observability/observability.py                                               |    21 |    4 |    17 |    81% |
| 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/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                                                             |    21 |    0 |    21 |   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/_credential_providers.py                                             |     4 |    0 |     4 |   100% |
| application_sdk/storage/_obstore_config.py                                                   |    13 |    0 |    13 |   100% |
| application_sdk/storage/_telemetry.py                                                        |     5 |    0 |     5 |   100% |
| application_sdk/storage/batch.py                                                             |    12 |    2 |    10 |    83% |
| application_sdk/storage/binding.py                                                           |    18 |    1 |    17 |    94% |
| application_sdk/storage/chunked.py                                                           |    10 |    0 |    10 |   100% |
| application_sdk/storage/cloud.py                                                             |    23 |    5 |    18 |    78% |
| application_sdk/storage/errors.py                                                            |    32 |   21 |    11 |    34% |
| 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                                                               |    27 |    1 |    26 |    96% |
| application_sdk/storage/preflight.py                                                         |     9 |    0 |     9 |   100% |
| application_sdk/storage/reference.py                                                         |    12 |    1 |    11 |    92% |
| application_sdk/storage/rolling.py                                                           |    32 |   12 |    20 |    62% |
| application_sdk/storage/rolling_errors.py                                                    |     4 |    0 |     4 |   100% |
| application_sdk/storage/transfer.py                                                          |    20 |    3 |    17 |    85% |
| application_sdk/storage/formats/__init__.py                                                  |    27 |    0 |    27 |   100% |
| application_sdk/storage/formats/format_errors.py                                             |    18 |    0 |    18 |   100% |
| application_sdk/storage/formats/json.py                                                      |    12 |    2 |    10 |    83% |
| application_sdk/storage/formats/parquet.py                                                   |    25 |    1 |    24 |    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                                                |    10 |    0 |    10 |   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                                                         |    31 |    0 |    31 |   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                                       |    26 |    5 |    21 |    81% |
| application_sdk/templates/contracts/sql_metadata.py                                          |    33 |    8 |    25 |    76% |
| application_sdk/templates/contracts/sql_query.py                                             |     7 |    0 |     7 |   100% |
| application_sdk/test_utils/integration/__init__.py                                           |     1 |    1 |     0 |     0% |
| application_sdk/testing/__init__.py                                                          |     1 |    0 |     1 |   100% |
| application_sdk/testing/_mustache.py                                                         |     2 |    0 |     2 |   100% |
| application_sdk/testing/fixtures.py                                                          |    10 |    0 |    10 |   100% |
| application_sdk/testing/mocks.py                                                             |    68 |   17 |    51 |    75% |
| application_sdk/testing/e2e/__init__.py                                                      |     1 |    0 |     1 |   100% |
| application_sdk/testing/e2e/_errors.py                                                       |    14 |    0 |    14 |   100% |
| application_sdk/testing/e2e/base.py                                                          |    26 |    1 |    25 |    96% |
| application_sdk/testing/e2e/client.py                                                        |    46 |    7 |    39 |    85% |
| application_sdk/testing/e2e/config.py                                                        |     2 |    0 |     2 |   100% |
| application_sdk/testing/e2e/credential.py                                                    |     2 |    0 |     2 |   100% |
| application_sdk/testing/e2e/logs.py                                                          |     6 |    1 |     5 |    83% |
| application_sdk/testing/e2e/payload.py                                                       |     9 |    0 |     9 |   100% |
| application_sdk/testing/e2e/pods.py                                                          |     5 |    1 |     4 |    80% |
| application_sdk/testing/e2e/portforward.py                                                   |     4 |    0 |     4 |   100% |
| application_sdk/testing/e2e/sql_app.py                                                       |     9 |    0 |     9 |   100% |
| application_sdk/testing/e2e/substitutions.py                                                 |     3 |    0 |     3 |   100% |
| application_sdk/testing/e2e/workflows.py                                                     |     3 |    0 |     3 |   100% |
| application_sdk/testing/full_dag/__init__.py                                                 |     1 |    0 |     1 |   100% |
| application_sdk/testing/full_dag/_errors.py                                                  |     1 |    0 |     1 |   100% |
| application_sdk/testing/full_dag/base.py                                                     |    17 |    1 |    16 |    94% |
| application_sdk/testing/full_dag/client.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/testing/full_dag/payload.py                                                  |     8 |    0 |     8 |   100% |
| application_sdk/testing/full_dag/sql_app.py                                                  |     5 |    0 |     5 |   100% |
| application_sdk/testing/hypothesis/__init__.py                                               |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/__init__.py                                    |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/sql_client.py                                  |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/clients/__init__.py                            |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/clients/sql.py                                 |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/common/__init__.py                             |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/common/logger.py                               |     3 |    0 |     3 |   100% |
| application_sdk/testing/hypothesis/strategies/handlers/__init__.py                           |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/__init__.py                       |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/sql_metadata.py                   |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/sql_preflight.py                  |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/__init__.py                             |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/json_input.py                           |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/parquet_input.py                        |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/outputs/__init__.py                            |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/outputs/json_output.py                         |     2 |    1 |     1 |    50% |
| application_sdk/testing/hypothesis/strategies/outputs/statestore.py                          |     3 |    1 |     2 |    67% |
| application_sdk/testing/hypothesis/strategies/server/__init__.py                             |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/server/fastapi/__init__.py                     |     1 |    1 |     0 |     0% |
| application_sdk/testing/integration/__init__.py                                              |     1 |    0 |     1 |   100% |
| application_sdk/testing/integration/_errors.py                                               |     6 |    0 |     6 |   100% |
| application_sdk/testing/integration/assertions.py                                            |    55 |   25 |    30 |    55% |
| application_sdk/testing/integration/client.py                                                |    18 |    0 |    18 |   100% |
| application_sdk/testing/integration/comparison.py                                            |    12 |    1 |    11 |    92% |
| application_sdk/testing/integration/lazy.py                                                  |    10 |    0 |    10 |   100% |
| application_sdk/testing/integration/models.py                                                |     9 |    0 |     9 |   100% |
| application_sdk/testing/integration/runner.py                                                |    26 |    2 |    24 |    92% |
| application_sdk/testing/integration/validation.py                                            |     7 |    0 |     7 |   100% |
| application_sdk/testing/parity/__init__.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/testing/parity/__main__.py                                                   |     2 |    1 |     1 |    50% |
| application_sdk/testing/parity/comparator.py                                                 |     8 |    0 |     8 |   100% |
| application_sdk/testing/parity/models.py                                                     |     5 |    1 |     4 |    80% |
| application_sdk/testing/parity/report.py                                                     |     4 |    0 |     4 |   100% |
| application_sdk/testing/scale_data_generator/__init__.py                                     |     1 |    0 |     1 |   100% |
| application_sdk/testing/scale_data_generator/config_loader.py                                |    11 |    4 |     7 |    64% |
| application_sdk/testing/scale_data_generator/data_generator.py                               |    10 |    3 |     7 |    70% |
| application_sdk/testing/scale_data_generator/driver.py                                       |     3 |    3 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/__init__.py                      |     1 |    1 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/base.py                          |     7 |    3 |     4 |    57% |
| application_sdk/testing/scale_data_generator/output_handler/csv_handler.py                   |     6 |    6 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/json_handler.py                  |     5 |    5 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/parquet_handler.py               |     6 |    6 |     0 |     0% |
| application_sdk/testing/sdr/__init__.py                                                      |     1 |    0 |     1 |   100% |
| application_sdk/testing/sdr/base.py                                                          |    14 |    3 |    11 |    79% |
| application_sdk/tools/__init__.py                                                            |     1 |    1 |     0 |     0% |
| application_sdk/tools/provision_credentials.py                                               |     2 |    1 |     1 |    50% |
| application_sdk/transformers/__init__.py                                                     |     4 |    2 |     2 |    50% |
| application_sdk/transformers/errors.py                                                       |     2 |    1 |     1 |    50% |
| application_sdk/transformers/atlas/__init__.py                                               |     6 |    1 |     5 |    83% |
| application_sdk/transformers/atlas/errors.py                                                 |     8 |    7 |     1 |    12% |
| application_sdk/transformers/atlas/sql.py                                                    |    25 |    4 |    21 |    84% |
| application_sdk/transformers/common/__init__.py                                              |     1 |    1 |     0 |     0% |
| application_sdk/transformers/common/last_sync.py                                             |     5 |    0 |     5 |   100% |
| application_sdk/transformers/common/utils.py                                                 |     6 |    0 |     6 |   100% |
| application_sdk/transformers/query/__init__.py                                               |    15 |    2 |    13 |    87% |
| application_sdk/transformers/query/errors.py                                                 |     5 |    3 |     2 |    40% |
| application_sdk/validation/__init__.py                                                       |     1 |    0 |     1 |   100% |
| application_sdk/validation/assets.py                                                         |    17 |    2 |    15 |    88% |
| contract-toolkit/examples/agent-e2e/app/generated/__init__.py                                |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_e2e_base.py                               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_e2e_credential.py                         |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_e2e_substitutions.py                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/agent-e2e/app/generated/_input.py                                  |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/__init__.py                        |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/_e2e_base.py                       |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/_e2e_substitutions.py              |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/behind-the-scenes/app/generated/_input.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/__init__.py                           |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/_e2e_base.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/_e2e_credential.py                    |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/crawler/_input.py                             |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/__init__.py                             |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/_e2e_base.py                            |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/_e2e_substitutions.py                   |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/bundle/app/generated/miner/_input.py                               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/connection-ref/app/generated/__init__.py                           |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/connection-ref/app/generated/_e2e_base.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/connection-ref/app/generated/_input.py                             |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/__init__.py                                   |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/_e2e_base.py                                  |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/_e2e_substitutions.py                         |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/deploy/app/generated/_input.py                                     |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/__init__.py                                    |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/_e2e_base.py                                   |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/_e2e_credential.py                             |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/fanin/app/generated/_input.py                                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/__init__.py                                     |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_e2e_base.py                                    |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_e2e_credential.py                              |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_e2e_substitutions.py                           |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/full/app/generated/_input.py                                       |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/__init__.py                                  |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/_e2e_base.py                                 |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/_e2e_substitutions.py                        |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/minimal/app/generated/_input.py                                    |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/__init__.py                                    |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/_e2e_base.py                                   |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/_e2e_substitutions.py                          |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/pools/app/generated/_input.py                                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/__init__.py                         |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_e2e_base.py                        |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_e2e_credential.py                  |     3 |    3 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_e2e_substitutions.py               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/publish-controls/app/generated/_input.py                           |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/__init__.py                                |     1 |    1 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/_e2e_base.py                               |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/_e2e_substitutions.py                      |     2 |    2 |     0 |     0% |
| contract-toolkit/examples/scheduled/app/generated/_input.py                                  |     2 |    2 |     0 |     0% |
| contract-toolkit/scripts/test-sdk-import.py                                                  |     5 |    1 |     4 |    80% |
| packages/conformance/conformance/__init__.py                                                 |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/cli.py                                                      |    13 |   11 |     2 |    15% |
| packages/conformance/conformance/bootstrap/__init__.py                                       |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/bootstrap/args.py                                           |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/bootstrap/autodetect.py                                     |     9 |    0 |     9 |   100% |
| packages/conformance/conformance/bootstrap/command.py                                        |    10 |    1 |     9 |    90% |
| packages/conformance/conformance/bootstrap/extract.py                                        |     6 |    0 |     6 |   100% |
| packages/conformance/conformance/bootstrap/render.py                                         |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/bootstrap/templates/build_conformance_args.py               |     3 |    2 |     1 |    33% |
| packages/conformance/conformance/renovate/__init__.py                                        |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/renovate/classify.py                                        |    10 |    1 |     9 |    90% |
| packages/conformance/conformance/renovate/models.py                                          |    10 |    2 |     8 |    80% |
| packages/conformance/conformance/renovate/scan.py                                            |    10 |    6 |     4 |    40% |
| packages/conformance/conformance/scorecard/__init__.py                                       |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/scorecard/cli.py                                            |     6 |    3 |     3 |    50% |
| packages/conformance/conformance/scorecard/compute.py                                        |     9 |    3 |     6 |    67% |
| packages/conformance/conformance/scorecard/readers.py                                        |     7 |    0 |     7 |   100% |
| packages/conformance/conformance/scorecard/rubric.py                                         |    10 |    5 |     5 |    50% |
| packages/conformance/conformance/scorecard/schema.py                                         |    14 |    2 |    12 |    86% |
| packages/conformance/conformance/scorecard/validate.py                                       |     4 |    1 |     3 |    75% |
| packages/conformance/conformance/suite/__init__.py                                           |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/suite/runner.py                                             |     9 |    2 |     7 |    78% |
| packages/conformance/conformance/suite/checks/__init__.py                                    |     1 |    1 |     0 |     0% |
| packages/conformance/conformance/suite/checks/_entrypoint_contract_fields.py                 |    13 |    4 |     9 |    69% |
| packages/conformance/conformance/suite/checks/_sdk_contract_mixins.py                        |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/_toolkit_baseline.py                           |     6 |    1 |     5 |    83% |
| packages/conformance/conformance/suite/checks/_version.py                                    |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/actions_pinning.py                             |    10 |    0 |    10 |   100% |
| packages/conformance/conformance/suite/checks/asyncio_loop_scope.py                          |    13 |    2 |    11 |    85% |
| packages/conformance/conformance/suite/checks/bootstrap_drift.py                             |    12 |    3 |     9 |    75% |
| packages/conformance/conformance/suite/checks/coverage_config.py                             |    12 |    3 |     9 |    75% |
| packages/conformance/conformance/suite/checks/dependency_conformance.py                      |    29 |    0 |    29 |   100% |
| packages/conformance/conformance/suite/checks/dev_entrypoint.py                              |     6 |    0 |     6 |   100% |
| packages/conformance/conformance/suite/checks/dockerfile_conformance.py                      |    17 |    1 |    16 |    94% |
| packages/conformance/conformance/suite/checks/e2e_agent_spec.py                              |     8 |    1 |     7 |    88% |
| packages/conformance/conformance/suite/checks/e2e_deployment_name.py                         |     9 |    3 |     6 |    67% |
| packages/conformance/conformance/suite/checks/generated_freshness.py                         |    23 |    0 |    23 |   100% |
| packages/conformance/conformance/suite/checks/gitignore_entries.py                           |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/suite/checks/integration_deselect.py                        |    11 |    2 |     9 |    82% |
| packages/conformance/conformance/suite/checks/integration_marking.py                         |    12 |    2 |    10 |    83% |
| packages/conformance/conformance/suite/checks/release_contract.py                            |     6 |    0 |     6 |   100% |
| packages/conformance/conformance/suite/checks/sdr.py                                         |    20 |    0 |    20 |   100% |
| packages/conformance/conformance/suite/checks/sdr_test_checks.py                             |    10 |    3 |     7 |    70% |
| packages/conformance/conformance/suite/checks/test_quality.py                                |    18 |    7 |    11 |    61% |
| packages/conformance/conformance/suite/checks/test_structure.py                              |    10 |    3 |     7 |    70% |
| packages/conformance/conformance/suite/checks/_ast_common/__init__.py                        |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_cli.py                            |     6 |    3 |     3 |    50% |
| packages/conformance/conformance/suite/checks/_ast_common/_directives.py                     |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_discovery.py                      |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_findings.py                       |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_imports.py                        |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_pytest_collection.py              |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_sanitizers.py                     |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_scope.py                          |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/suite/checks/_ast_common/_toml_suppress.py                  |     4 |    1 |     3 |    75% |
| packages/conformance/conformance/suite/checks/app_name_alignment/__init__.py                 |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/app_name_alignment/_check.py                   |     3 |    1 |     2 |    67% |
| packages/conformance/conformance/suite/checks/app_name_alignment/_code_app_name.py           |    12 |    0 |    12 |   100% |
| packages/conformance/conformance/suite/checks/app_name_alignment/_contract_app_name.py       |     8 |    0 |     8 |   100% |
| packages/conformance/conformance/suite/checks/client_seam/__init__.py                        |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/client_seam/_raw_http_to_atlan.py              |    10 |    0 |    10 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/__init__.py                        |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/_authoring.py                      |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/_consumer.py                       |     7 |    3 |     4 |    57% |
| packages/conformance/conformance/suite/checks/deprecation/_contract_compat.py                |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/deprecation/_extractor.py                      |    18 |    5 |    13 |    72% |
| packages/conformance/conformance/suite/checks/deprecation/_ledger_schema.py                  |     7 |    1 |     6 |    86% |
| packages/conformance/conformance/suite/checks/deprecation/_manifest.py                       |    10 |    1 |     9 |    90% |
| packages/conformance/conformance/suite/checks/determinism/__init__.py                        |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/determinism/_p020_primitives.py                |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/determinism/_p021_io.py                        |     3 |    1 |     2 |    67% |
| packages/conformance/conformance/suite/checks/determinism/_p022_unawaited.py                 |     8 |    5 |     3 |    38% |
| packages/conformance/conformance/suite/checks/determinism/_p023_blocking_async.py            |    11 |    9 |     2 |    18% |
| packages/conformance/conformance/suite/checks/determinism/_p024_sync_atlan_client.py         |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/determinism/_p031_executor_offload.py          |     4 |    1 |     3 |    75% |
| packages/conformance/conformance/suite/checks/determinism/_p036_process_isolation.py         |     3 |    1 |     2 |    67% |
| packages/conformance/conformance/suite/checks/determinism/_workflow_methods.py               |     7 |    0 |     7 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/__init__.py                         |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/_bootstrap_common.py                |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/_server_bootstrap.py                |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint/_worker_bootstrap.py                |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/__init__.py               |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/_check.py                 |     5 |    1 |     4 |    80% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/_code_entrypoints.py      |    11 |    0 |    11 |   100% |
| packages/conformance/conformance/suite/checks/entrypoint_alignment/_contract_entrypoints.py  |     5 |    1 |     4 |    80% |
| packages/conformance/conformance/suite/checks/error_handling/__init__.py                     |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/error_handling/_checker.py                     |    14 |   12 |     2 |    14% |
| packages/conformance/conformance/suite/checks/error_handling/_collect.py                     |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/error_handling/_constants.py                   |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/suite/checks/error_handling/_helpers.py                     |    21 |    3 |    18 |    86% |
| packages/conformance/conformance/suite/checks/error_handling/exception_chaining.py           |     5 |    3 |     2 |    40% |
| packages/conformance/conformance/suite/checks/error_handling/http_failure.py                 |    11 |    5 |     6 |    55% |
| packages/conformance/conformance/suite/checks/error_handling/security.py                     |     4 |    1 |     3 |    75% |
| packages/conformance/conformance/suite/checks/error_handling/silent_swallow.py               |    12 |    9 |     3 |    25% |
| packages/conformance/conformance/suite/checks/error_handling/untyped_raise.py                |     7 |    5 |     2 |    29% |
| packages/conformance/conformance/suite/checks/legacy_contract/__init__.py                    |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/legacy_contract/_directives_pkl.py             |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/legacy_contract/_scan.py                       |     8 |    1 |     7 |    88% |
| packages/conformance/conformance/suite/checks/logging/__init__.py                            |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/logging/_base.py                               |     3 |    1 |     2 |    67% |
| packages/conformance/conformance/suite/checks/logging/_checker.py                            |    15 |   10 |     5 |    33% |
| packages/conformance/conformance/suite/checks/logging/_config.py                             |     6 |    0 |     6 |   100% |
| packages/conformance/conformance/suite/checks/logging/_constants.py                          |     1 |    0 |     1 |   100% |
| packages/conformance/conformance/suite/checks/logging/_crossfile.py                          |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/logging/_format.py                             |     8 |    0 |     8 |   100% |
| packages/conformance/conformance/suite/checks/logging/_helpers.py                            |    39 |   20 |    19 |    49% |
| packages/conformance/conformance/suite/checks/logging/_level.py                              |     5 |    0 |     5 |   100% |
| packages/conformance/conformance/suite/checks/logging/_performance.py                        |    19 |   13 |     6 |    32% |
| packages/conformance/conformance/suite/checks/logging/_print.py                              |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/logging/_security.py                           |     3 |    0 |     3 |   100% |
| packages/conformance/conformance/suite/checks/logging/_toml.py                               |     5 |    1 |     4 |    80% |
| packages/conformance/conformance/suite/checks/logging/_traceback.py                          |     4 |    0 |     4 |   100% |
| packages/conformance/conformance/suite/checks/manifest_app_name/__init__.py                  |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/manifest_app_name/_check.py                    |     8 |    1 |     7 |    88% |
| packages/conformance/conformance/suite/checks/manifest_contract/__init__.py                  |     2 |    0 |     2 |   100% |
| packages/conformance/conformance/suite/checks/manifest_contract/_check.py                    |     4 |    0 |     4 |   100% |
|

This message was truncated. Download full message

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📦 Trivy Vulnerability Scan Results

Schema Version Created At Artifact Type
2 2026-08-10T17:48:54.64520933Z . repository

Report Summary

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

Scan Result Details

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

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📦 Trivy Secret Scan Results

Schema Version Created At Artifact Type
2 2026-08-10T17:49:02.664416724Z . repository

Report Summary

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

Scan Result Details

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

Comment thread application_sdk/handler/service.py Fixed
@atlan-app-fleet

atlan-app-fleet Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

☂️ Code Coverage

current status: ✅

Overall Coverage

Statements Covered Coverage Threshold Status
20890 19059 91% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: c32723e by action🐍

@Aryamanz29

Copy link
Copy Markdown
Member Author

@sdk-resolve

…injection)

The route validates the name before calling, but the helper must be
locally safe: regex-reject path metacharacters and prove containment
under CONTRACT_GENERATED_DIR before touching disk.
@github-actions

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve started. Driving this PR toward merge-ready — fixing CI + every @sdk-review finding, then requesting human review.

Follow progress →

This runs out-of-band and can take several minutes; I'll comment here when it finishes.

Comment thread application_sdk/handler/service.py Fixed
Comment thread application_sdk/handler/service.py Fixed
…ution

CodeQL's taint engine does not model pathlib.is_relative_to as a
py/path-injection sanitizer, so the prior .resolve()+is_relative_to guard
still raised 2 high alerts. Switch to the repo's proven pattern (mirrors
_validated_temp_path): os.path.realpath + explicit prefix containment,
which CodeQL recognizes as a sanitizer. Behavior is unchanged — the regex
still rejects path metacharacters and the resolved path must stay under
CONTRACT_GENERATED_DIR.
@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@sdk-review

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Earlier @sdk-review trigger (click to expand)

🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T11:33:23.875Z.

Watch the workflow run live — the review summary will appear as a separate comment when complete (typical: 5–30 min, hard cap 2h).


Completed — status completed, cost $4.748682000000001, duration 10m 8s.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Review (mothership): PR #3090 — fix(handler): filesystem-resolve bundle marketplace entrypoints for input-contract and bare-manifest

Verdict: READY TO MERGE

This PR closes a Sev1 gap where bundle ("uber") apps 404 on /input-contract?entrypoint=<marketplace-name> and on bare /manifest, because marketplace entry points exist only as generated contract dirs on disk while the registry holds their DAG-node workflows. The fix extends the same filesystem-first authority the manifest and configmap routes already have to the two remaining surfaces, with defensive name validation and realpath containment before any disk or import touch. Registry hits keep winning and apps without generated dirs are byte-for-byte unchanged — this treats the cause (two route surfaces lagging the filesystem-first design), not the symptom.


Findings

No findings. Two candidate findings from the review agents were verified against the code and withdrawn:

  • A test-isolation finding claiming the new TestBundleMarketplaceEntrypoints must use the clean_app_registry fixture: this 7,282-line test file never uses that fixture (0 occurrences) and its established convention is manual AppRegistry.reset() in setup_method/teardown_method — exactly what the new tests do. Withdrawing per file-local convention and behavioral equivalence.
  • A finding that the except HTTPException fallback in get_input_contract could mask an app-not-registered 404 when a stale generated dir is on disk: this requires a misconfigured handler (app name absent from the registry), in which state the whole service is already broken, and the served contract comes from the app's own deployed bundle. Disk-first on registry miss is the PR's stated design, matching the manifest/configmap routes. Below the reporting bar.

Holistic Recommendations

None — the approach is correct. One optional (non-blocking) observation: _published_input_contract and _marketplace_entrypoint_contract now carry near-identical import-and-duck-type loops over different module-path candidates; a shared helper taking the candidate tuple would remove the duplication if a third caller ever appears.

Strengths

  • Defense in depth on the new helper: independent regex re-validation, realpath containment check before isfile, and import targets confined to canonical app package paths — the path-injection concern is handled locally and provably.
  • Failure-safe fallback semantics: ImportError per candidate continues to the next, no contract module means the original 404 is re-raised, and registry hits still win — existing apps are untouched.
  • Tests pin the important behaviors: disk-resolved contract 200, both 404-preservation cases (no generated dir, dir without importable contract — never a 500, never a wrong sibling's schema), and deterministic alphabetical bare-manifest selection. They restore mutated module state (CONTRACT_GENERATED_DIR, sys.modules) in finally blocks.
  • Comments explain the marketplace-vs-registry distinction at both call sites, so the next reader understands why the filesystem fallback exists.

CI: all passing
Models: kimi-k3 (review) + adversarial skipped (Wave 1 produced zero findings after verification)
Cross-model agreement: n/a
Run: view workflow logs + cost

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve — final report. This PR is merge-ready; a human needs to take it from here (resolver never merges).

Outcome: merge-ready ✅ — green CI, zero open findings (nits included), verdict READY_TO_MERGE on the current HEAD (4486815).

Details:

  • Rounds taken: 1 (review comment, verdict READY_TO_MERGE, zero findings — two candidate findings were verified against the code and withdrawn by the reviewer itself).
  • Findings fixed: 0 · dismissed: 0 — nothing was open.
  • CI: all 56 required checks green (6 skipped by design); sdk-review check: Approved.
  • The reviewer left one optional, non-blocking observation: _published_input_contract and the new _marketplace_entrypoint_contract carry near-identical import-and-duck-type loops — a shared helper would remove the duplication if a third caller ever appears. Not required for merge.

What is left for you:

  • No reviewer list is configured (vars.SDK_RESOLVE_REVIEWERS is unset), so the resolver could not auto-request one — please assign a human reviewer.
  • Merge is deliberately left to a human.

@Aryamanz29 — your turn.

@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 the RCA write-up — the table showing which of the four routes work today is exactly the right framing, and the input-contract 404 is a real break that needs unblocking.

My concern is not this diff's correctness but its direction, and I think there's a fact that changes the calculus: App.pkl already declares every entry point explicitly, and nothing in the SDK runtime reads that declaration.

The declaration already exists

  • open class Entrypoint (contract-toolkit/src/App.pkl:1514) carries name — the marketplace name, i.e. precisely the identity _resolve_app_entrypoint can't see — plus packageId, contract, and the display metadata.
  • packageId already encodes the distinction this PR is trying to recover by asking "does a directory exist": per its own doc, "Entrypoints without a packageId are always routable via ?entrypoint= but do not appear as marketplace cards."
  • effectiveEntrypoints (:3473) synthesises the implicit entry point for single-entrypoint apps from the bundle-level fields, so the list is uniformly populated for both the flat and nested shapes.
  • renderEntrypoint (:3387) emits it into atlan.yaml, and the doc comment at :201 says the block is "still emitted for routing".

Meanwhile AppRegistry is built solely from _scan_entrypoints() in application_sdk/app/_ep_registration.py@entrypoint decorators only. grep atlan.yaml application_sdk/ returns nothing but Dapr-drift log strings in main.py.

So CONTRACT_GENERATED_DIR.glob("*/manifest.json") infers at request time a list the contract already states declaratively one file over. The same is true of #2776's alphabetical registry walk. We have two registries with different membership — the decorator registry and the app/generated/*/ filesystem — and #2764, #2776 and this PR each teach one more route to guess at the other. Bare /manifest is now at 7 candidate sources; /input-contract at 5 module candidates.

The glob isn't a faithful proxy for the declaration

Those per-entry-point dirs are written only when (entrypoint.contract != null) and gated on emitGeneratedArtifacts (App.pkl:3656). With that flag off, entry points stay declared and routable while the new fallback silently finds nothing — a 404 that looks identical to the one being fixed here, with no signal pointing at the cause. The glob also can't distinguish a marketplace card from a DAG-node artifact dir. packageId can.

One specific request on this diff

I'd take the input-contract half — it unblocks the break, and the two-import-path guesswork is bounded and documented.

I'd drop candidate #7 (the bare-manifest disk glob). Picking alphabetically among all on-disk entry points means a bundle with N marketplace entry points now answers a bare GET /manifest with a confidently wrong manifest instead of 404-ing. The PR frames that as "deterministic instead of 404-ing", but deterministic-and-arbitrary isn't correct — for submit-time pre-validation, "you must specify an entrypoint" is the more honest answer, and a wrong-manifest bug will be considerably harder to diagnose than the 404 it replaces.

Follow-up

Filed as FND-180 for the durable version, which is small and retires far more than it adds:

  1. Toolkit emits effectiveEntrypoints as app/generated/entrypoints.json. It has to go there rather than being read from atlan.yamlatlan.yaml isn't reliably in the runtime image (the app template does COPY . ., but several apps copy only app/), whereas app/generated/ ships in every case.
  2. Toolkit emits each entry point's input schema as JSON next to its manifest.json, so /input-contract stops importing Python at request time — no importlib, no sys.modules mutation in tests, no circular-import dance, no cold-start cost. That also makes the kebab-dir _input.py relocation irrelevant to the SDK, which is worth doing on its own: conformance already carries a duplicate (root / "app").rglob("_input.py") fallback in checks/sdr.py:704 for the same relocation.
  3. One resolver reads the declared list; the decorator registry keeps its distinct job of saying which method runs for the entry points that have one.

That splits the two questions cleanly — the contract owns which entry points exist, the registry owns what executes — and lets us delete the #2764 flat fallback, the #2776 alphabetical walk, and both branches added here.

Not blocking on the follow-up, and no objection to landing the unblock under time pressure. Two asks: drop candidate #7, and add a comment pointing at FND-180 on the code that stays so the next person hitting this doesn't add candidate #8.

@linear

linear Bot commented Aug 10, 2026

Copy link
Copy Markdown

FND-180

Per review: serving one of N marketplace manifests picked alphabetically
on a bare /manifest call is confidently wrong rather than helpfully
deterministic — callers must name the entry point, and the honest answer
when they don't is the existing 404. The input-contract half (the actual
/v1/app break) stays. The declarative resolver that can answer the bare
call properly — toolkit-emitted entrypoints.json — is FND-180.
@Aryamanz29

Copy link
Copy Markdown
Member Author

Thanks @cmgrote — agreed on all three points, and the review request is applied as of 4cd62d5:

  • Dropped the bare-manifest disk glob (candidate Improvements #7). The replacement test now asserts the bare call keeps 404-ing on a bundle even when entry-point dirs exist on disk, with a pointer to FND-180 as the thing that can answer it properly.
  • Kept the input-contract half unchanged (plus the CodeQL path-containment hardening — all three alerts are resolved on the current head).
  • Dropping the glob doesn't weaken the /v1/app unblock: with the contract resolving, Heracles carries the entrypoint through the whole create→manifest flow, so no bare call occurs — and bundle submits via package-workflows (which always name the entrypoint) already work in production today.

+1 on FND-180's shape — the entrypoints.json + static JSON schemas design retires the two-import-path guesswork here as well, and I'd be glad to take the consumer-side migration (this route + deleting the #2764/#2776 fallbacks) when the toolkit emit lands. The relocation-irrelevance point is a nice bonus: csa-uber's regen.sh moves _input.py around precisely because the kebab dirs aren't importable, and static JSON makes that whole dance unnecessary.

@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@sdk-resolve

@github-actions

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve started. Driving this PR toward merge-ready — fixing CI + every @sdk-review finding, then requesting human review.

Follow progress →

This runs out-of-band and can take several minutes; I'll comment here when it finishes.

@Aryamanz29

Copy link
Copy Markdown
Member Author

Live validation on a test tenant (branch image 702d01e, deployed via GM targeted release)

Same calls, same deployed app service, before → after:

Route Before (main-7e73524) After (this PR + application-sdk#3090)
GET /workflows/v1/input-contract?entrypoint=asset-export-basic 404 "No input contract for entrypoint" 200 — serves the new AppInputContract
GET /workflows/v1/input-contract?entrypoint=admin-export 404 200
GET /workflows/v1/input-contract?entrypoint=asset-export-advanced 404 200
GET /workflows/v1/manifest?entrypoint=asset-export-basic 200 200 (unchanged)
GET /workflows/v1/input-contract?entrypoint=does-not-exist 404 404 (contract preserved)

The first row is the exact break behind the POST /v1/app 500 (app validation error: 404 - No manifest available): Heracles' create flow died on the contract fetch, and the fallback path then hit the bare manifest 404.

End-to-end POST /v1/app create+run on the test tenant is running next; result will be posted here.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@sdk-review

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Earlier @sdk-review trigger (click to expand)

🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T13:25:37.549Z.

Watch the workflow run live — the review summary will appear as a separate comment when complete (typical: 5–30 min, hard cap 2h).


🟥 Run ended without a clean completion — status skipped, cost $unknown, duration 3m 19s.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ SDK Review couldn't start due to a transient infrastructure issue.

The review environment was temporarily unreachable. This is usually transient.

View workflow logs

Please retry by commenting @sdk-review again. If it keeps failing, reach out to the platform team.

@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@sdk-resolve

@github-actions

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve started. Driving this PR toward merge-ready — fixing CI + every @sdk-review finding, then requesting human review.

Follow progress →

This runs out-of-band and can take several minutes; I'll comment here when it finishes.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ SDK Resolve failed: could not connect to VPN.

All 3 VPN connection attempts to vpn2.atlan.app failed. This is usually a transient infrastructure issue.

View workflow logs

Please retry by commenting @sdk-resolve again. If it keeps failing, flag it in #bu-platform-engineering.

@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@sdk-resolve

@github-actions

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve started. Driving this PR toward merge-ready — fixing CI + every @sdk-review finding, then requesting human review.

Follow progress →

This runs out-of-band and can take several minutes; I'll comment here when it finishes.

@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@sdk-review

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Earlier @sdk-review trigger (click to expand)

🔍 SDK Review (mothership) triggered by @cmgrote at 2026-08-10T14:52:22.555Z.

Watch the workflow run live — the review summary will appear as a separate comment when complete (typical: 5–30 min, hard cap 2h).


🟥 Run ended without a clean completion — status unknown, cost $unknown, duration 10m 16s.

@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@sdk-resolve

@github-actions

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve started. Driving this PR toward merge-ready — fixing CI + every @sdk-review finding, then requesting human review.

Follow progress →

This runs out-of-band and can take several minutes; I'll comment here when it finishes.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@sdk-review

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Earlier @sdk-review trigger (click to expand)

🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T17:26:55.063Z.

Watch the workflow run live — the review summary will appear as a separate comment when complete (typical: 5–30 min, hard cap 2h).


Completed — status completed, cost $4.097466000000002, duration 10m 3s.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Re-review (mothership): PR #3090 — fix(handler): filesystem-resolve bundle marketplace entrypoints for input-contract and bare-manifest

Verdict: READY TO MERGE

The delta since the last approved round is one commit implementing exactly the prior round's lone Nit: discovery-API failures (auth/transport/unparseable JSON) no longer masquerade as the routine "no qualifying run" case. discover() now returns a third error flag — true when the run list itself failed or every candidate probe errored, false for a healthy-but-empty window — and main() emits it as a discovery_error GITHUB_OUTPUT while still exiting 0, so the best-effort publish stays green and the workflow can warn loudly on the operational fault. The workflow-side consumer step is deliberately left to a human (sandbox token cannot push workflow files); until it lands the output is inert, which is safe. Nothing blocking.


Delta from previous review

  • Resolved (1): discovery-API failures warn-and-skip green → discovery_error=true|false output now distinguishes operational faults from the routine empty case; 6 new tests pin all four error paths plus the healthy-empty path.
  • Still present (0)
  • New (0)
  • Downgraded (0)

Findings

None.

Holistic Recommendations

None — the error predicate is the right shape: probed > 0 and probe_errors == probed means an empty run list (zero candidates) is routine, a single successful artifact listing (even with zero live SARIF) proves the API is healthy, and only a total discovery blackout raises the flag. The output is written only in the no-run branch, which is the only place a consumer needs it; in found-run branches the unset output reads as empty (≠ 'true'), matching the documented "always false when a run was found" contract.

Strengths

  • The implementation matches the Nit's suggested path exactly (distinct output rather than exit 2), keeping the publish gate's exit-0 contract unchanged.
  • Test coverage is exhaustive for the new logic: run-list failure, unparseable run list, all-probes-fail, partial-probe-failure recovery, healthy-empty, and end-to-end output wiring — 30/30 pass on this HEAD.
  • Docstring and output contract stay in sync: discovery_error is documented as true only on operational faults and the commit message states plainly which follow-up step a human must add to .github/workflows/update-dashboard.yaml.

CI: no failures (sdk-review pending — this run; several optional legs skipped as usual)
Models: kimi-k3 (review) + adversarial skipped (Wave 1 produced zero findings)
Cross-model agreement: n/a — no findings to challenge
Run: view workflow logs + cost

atlan-ci
atlan-ci previously approved these changes Aug 10, 2026

@atlan-ci atlan-ci 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.

SDK reviewer's verdict: READY TO MERGE.

Full review summary is in the comment posted on this PR.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve — final report. This PR is merge-ready; a human needs to take it from here (the resolver never merges).

Outcome: merge-ready ✅ — green CI, zero open findings (nits included), verdict READY_TO_MERGE.

⚠️ Reviewer assignment: NO reviewer list is configured (vars.SDK_RESOLVE_REVIEWERS is unset), so I could not auto-request reviewers — a human needs to assign one.


Rounds this run: 1 review round (round 3 overall on this PR).

Findings:

  • The lone open nit from the prior round (fetch_conformance_sarif.py — discovery failure vs no-data conflation) was already resolved by commit 02416a4 before this run started: discover() now emits a distinct discovery_error GITHUB_OUTPUT while keeping the publish gate exit-0. This run's re-review confirmed it: Findings: None, verdict READY_TO_MERGE on HEAD 02416a4.
  • Findings fixed: 0 (none were open this run) · dismissed: 0.

One human follow-up (non-blocking): the discovery_error output the script now emits is consumed by a warn-step that belongs in .github/workflows/update-dashboard.yaml. That step cannot land from this sandbox — pushes touching workflow files are rejected (refusing to allow a GitHub App ... without workflows permission). The reviewer confirmed leaving it to a human is safe: until the step exists the output is simply inert (no behavior change). The step to add, right after the Discover + download Conformance SARIF step:

      - name: Warn on conformance discovery failure
        if: steps.download.outputs.discovery_error == 'true'
        run: |
          echo "::warning::Conformance SARIF discovery failed (gh run list / artifact probe error) — dashboard may be stale."

Final CI: all required checks pass on HEAD 02416a4 (only expected optional e2e/container legs skip). Verdict: READY_TO_MERGE.

Merge is yours — I do not merge.

@cmgrote — it's your turn.

`fetch_conformance_sarif.py` now emits a distinct `discovery_error`
output so a failed `gh run list` / artifact probe is separable from a
repo that genuinely has no conformance data yet. Consume it: annotate the
run with a warning when discovery itself failed, so a stale conformance
panel is attributable instead of silently indistinguishable from
"nothing to publish".

The step could not land with the rest of the change — pushes touching
`.github/workflows/**` are rejected for the resolver app (no `workflows`
permission), so the output was inert until now.
@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@sdk-review

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🔍 SDK Review (mothership) triggered by @cmgrote at 2026-08-10T17:49:02.669Z.

Watch the workflow run live — the review summary will appear as a separate comment when complete (typical: 5–30 min, hard cap 2h).


Completed — status completed, cost $1.2472020000000001, duration 8m 17s.
Error: unknown — {"type":"error","message":"ReadableStream received over RPC disconnected prematurely."}

cmgrote added a commit that referenced this pull request Aug 10, 2026
…ic file

The dynamic-manifest hook replaces the manifest wholesale — `raw =
orjson.dumps(computed)` discarded the reconciled bytes — so everything it
emitted was served unreconciled: a task_queue, a freshly generated node, a
token it re-introduced. FND-195's guarantee that the served queue is stamped
from what the worker polls therefore did not hold for any app with a hook.

That is the worst possible population to miss. A bundle app's marketplace
entry points have their DAG computed per submission by exactly this hook, and
those are the CONNECT-183-shaped apps the guarantee exists for. #3090 widens
the exposure further: before it, a bare /manifest on a bundle 404'd; after it,
that route serves hook-computed DAGs through this same path.

Not a regression introduced here — the pre-FND-195 code also substituted
{deployment_name} before the hook and kept nothing afterwards. It only becomes
load-bearing once the queue value is the thing being guaranteed.

The pre-hook pass stays, so the hook still sees resolved values it may key on;
the second pass is idempotent (no template survives the first, so it is a
no-op unless the hook introduced something new).

Catches unresolved *tokens*, not a hook that hardcodes a concrete-but-wrong
queue: that string has no token to match, and normalising every `atlan-*`
queue would rewrite the legitimate cross-app dispatch nodes this deliberately
leaves alone. Conformance O005 (#3094) is the guard for that shape.

Both tests were checked against the pre-change code and fail there. The
unresolvable-name test asserts the graded outcome — deployment token filled,
{app_name} left visible — because asserting only the surviving token would
also pass against a build that never reconciles hook output.

Refs FND-195.
@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Re-review (mothership): PR #3090 — fix(handler): filesystem-resolve bundle marketplace entrypoints for input-contract and bare-manifest

Verdict: READY TO MERGE

The delta since the last approved round is a single commit landing exactly the follow-up that round called out: the workflow-side consumer of the new discovery_error output. .github/workflows/update-dashboard.yaml gains a "Warn on conformance discovery failure" step gated on steps.download.outputs.discovery_error == 'true' that emits a ::warning:: annotation, so a failed gh run list / artifact probe now surfaces loudly instead of letting the dashboard go silently stale, while the publish itself stays green and best-effort. Verified the wiring end-to-end against this HEAD: the script writes discovery_error=true|false only in the no-qualifying-run branch (fetch_conformance_sarif.py:272), which is precisely the case this step covers; in every other branch the unset output reads as empty (≠ 'true'), so the warning cannot false-fire on a healthy publish. Nothing blocking.


Delta from previous review

  • Resolved (0)
  • Still present (0)
  • New (0)
  • Downgraded (0)
  • Delta scope: 1 commit, 1 file (.github/workflows/update-dashboard.yaml, +10/−0) — the human-applied workflow consumer the prior round explicitly deferred.

Findings

None.

Holistic Recommendations

None. This closes the loop opened two rounds ago: (1) discovery stopped conflating "no data" with "discovery broke" by probing newest-first and exposing discovery_error; (2) this round wires the annotation so the operational fault is attributable from the workflow run page. The ::warning:: (not ::error::) severity is the right call — a stale dashboard panel must not fail a best-effort publish.

Strengths

  • The comment block above the new step documents why has_artifacts == 'false' is ambiguous and which of the two cases this step surfaces — the next maintainer does not have to re-derive the contract.
  • The gate condition matches the producer's contract exactly: warn only on operational fault, stay silent on the routine healthy-but-empty case and on every successful-publish path.
  • Zero-risk addition: no new secrets, no interpolated expressions in run:, no change to any existing step's behavior.

CI: no failures
Models: kimi-k3 (review) + adversarial skipped (Wave 1 produced zero findings)
Cross-model agreement: n/a — no findings to challenge
Run: view workflow logs + cost

@cmgrote
cmgrote enabled auto-merge August 10, 2026 18:03
@cmgrote
cmgrote added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 02bed05 Aug 10, 2026
65 checks passed
@cmgrote
cmgrote deleted the aryaman/bundle-entrypoint-registry branch August 10, 2026 18:13
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.

4 participants