Skip to content

fix(task-queue): derive the queue name once, and stamp it into the served manifest - #3101

Merged
cmgrote merged 6 commits into
mainfrom
chrishehim/fnd-195
Aug 10, 2026
Merged

fix(task-queue): derive the queue name once, and stamp it into the served manifest#3101
cmgrote merged 6 commits into
mainfrom
chrishehim/fnd-195

Conversation

@cmgrote

@cmgrote cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Problem

The Temporal task-queue name was computed twice, independently, by two code paths that must agree exactly and were not forced to:

  1. the worker, via main._derive_task_queue(), reading ATLAN_APPLICATION_NAME / ATLAN_DEPLOYMENT_NAME and branching on truthiness;
  2. the served manifest, whose resolved task_queue the Automation Engine writes into the DAG and therefore submits work to — token-filled from atlan-{app_name}-{deployment_name} at serve time.

When they disagree nothing fails loudly. AE submits to one queue, the worker polls another, and the run sits unclaimed until its 24h heartbeat backstop — CONNECT-183. The same gap stripped failure attribution in HYP-1954, and has been independently hand-patched at least four times outside this repo (Heracles, native-migration-app, atlan-local-marketplace-app / CONNECT-191, atlan-hightouch-app / ARUN-1039), plus one shipped double-prefix bug (DISTR-834) from a caller that prefixed an already-prefixed value.

Given ATLAN_DEPLOYMENT_NAME=prod, only the first row was safe:

ATLAN_APPLICATION_NAME Worker polls Manifest resolved to Agree?
dbt atlan-dbt-prod atlan-dbt-prod yes
dbt, no deployment name dbt (bare, unprefixed) atlan-dbt-default no
unset <ClassName>-queue atlan-default-prod no

The other two produce a plausible-looking queue name that nothing polls.

Fix

One derivation. New application_sdk/common/task_queue.py holds the rule. main._derive_task_queue, the manifest-serve path, create_app_handler_service's own {app_name}-queue default, and the e2e agent_spec mirror all route through it — four in-repo implementations collapsed to one.

The manifest doesn't re-derive, it stamps. Two paths deriving the same answer only agree while their inputs agree, and the inputs are exactly what drifts. The handler passes resolve_manifest_tokens the queue it was configured with — the same value create_worker receives, in both combined and split-deployment mode — and the served task_queue is stamped from it. That closes two cases plain re-derivation cannot reach:

  • an explicit ATLAN_TASK_QUEUE / --task-queue override (no derivation reproduces it);
  • a baked contract name that no longer matches the deployment's ATLAN_APPLICATION_NAME — the DISTR-834 / CONNECT-191 shape, where the manifest serves a plausible atlan-dbt-prod while the worker polls atlan-dbt-v3-prod.

The hook's output is reconciled too, not just the static file. compute_manifest replaces the manifest wholesale, so reconciling only the file on disk left everything the hook emitted unreconciled — and a bundle's marketplace entry points have their DAG computed per submission by exactly that hook, i.e. the guarantee was missing for the population it exists for. The pre-hook pass stays (the hook should see resolved values it may key on) and the second pass is idempotent. It catches unresolved tokens, not a hook that hardcodes a concrete-but-wrong queue — that has no token to match, and normalising every atlan-* queue would rewrite the legitimate cross-app dispatch nodes below; #3094's O005 is the guard for that shape.

The queue template is matched as a unit, not token-filled. Filling {app_name} and {deployment_name} separately is the divergence: with no deployment name the worker drops the prefix and polls a bare <app>, while filling in place yields atlan-<app>-local.

The unset case stays loud. constants.APPLICATION_NAME's "default" is now documented as scoped to identity uses (object-store prefixes like persistent-artifacts/apps/<name>/, log tagging) where a missing segment would break paths — and excluded from queue naming, since atlan-default-prod reads as a legitimate queue, is polled by nobody, and reproduces the original hang. Loudness is graded, because one token hides two different failures:

  • an {app_name} the SDK can fill from the registered app name (what the toolkit would have baked) is filled and logged at WARNING — the response is correct, but the app's committed manifest is stale and the next writer of that DAG gets no backstop;
  • with nothing to fill it from, the literal token is served and logged at ERROR. A literal {app_name} in the DAG is greppable and diagnosable in one step.

Deliberately preserved

  • {ClassName}-queue when no app name exists — it predates the env-var convention and is load-bearing for local dev. The manifest now stamps that same queue, so a local full-DAG run lands where the local worker actually polls.
  • DAG nodes dispatching to another app's queue (atlan-publish-{deployment_name}, and the QI / popularity / lineage nodes in App.pkl) are token-filled and otherwise left alone — not normalised, and not warned about, since a warning there would fire on every correct multi-app DAG.
  • The {deployment_name} token's existing fallback for non-queue uses.

Not in this PR

No contract-toolkit change. Both baking the fully-derived value and the inverse (a single {task_queue} token the SDK resolves) change the manifest wire format, and the four out-of-repo consumers that hand-patched this gap substitute {app_name} / {deployment_name} themselves — emitting a token they don't know about breaks them. That's a coordinated cross-repo rollout, not a line here. Keeping the wire format and moving reconciliation into the serve path gets the same guarantee with no downstream break; worth revisiting once those consumers are retired.

Relationship to open PRs

Test plan

  • 23 new tests in tests/unit/common/test_task_queue.py, written to assert equality between the worker and manifest paths rather than two independently hard-coded expectations — so a future change to the rule can't satisfy one side and quietly break the other. Covers all three rows of the table, both the un-baked and toolkit-baked template shapes, the explicit-override and baked-name-disagrees cases, and the residual-token behaviour.
  • 6 route-level regressions in tests/unit/handler/test_service.py: served queue matches the configured worker queue when the baked name disagrees with env, and unresolvable {app_name} keeps the literal token and never manufactures default — each asserted on all three manifest sources: the disk path, the programmatic AppManifest path (which previously served model_dump_json() verbatim), and the compute_manifest output (which previously discarded the reconciled bytes via raw = orjson.dumps(computed)). The two hook tests were both checked against the pre-change code and fail there; the unresolvable-name one asserts the graded outcome — deployment token filled, {app_name} left visible — since asserting only the surviving token would also pass against a build that never reconciles hook output.
  • uv run pytest tests/unit — 6369 passed, 9 skipped
  • Conformance suite — exit 0, no findings in the new or changed files
  • uv run pre-commit run --files … (ruff, ruff-format, isort, pyright) — clean

Closes FND-195.

🤖 Generated with Claude Code

…rved manifest

The Temporal task-queue name was computed twice, independently, by two code
paths that must agree exactly and were not forced to: the worker's
_derive_task_queue(), and the manifest served to the Automation Engine, which
token-filled `atlan-{app_name}-{deployment_name}`. When they disagreed nothing
failed loudly — AE submitted to one queue, the worker polled another, and the
run sat unclaimed until its 24h heartbeat backstop (CONNECT-183; the same gap
stripped failure attribution in HYP-1954).

The rule now lives in one place, application_sdk.common.task_queue, and the
manifest route does not re-run it: it stamps the queue the handler was
configured with, which is the same value create_worker receives. Two paths
deriving the same answer is a convention that holds until their inputs differ;
one path copying the other's answer is structural. That also covers what
re-derivation cannot reach — an explicit ATLAN_TASK_QUEUE override, and a baked
contract name that no longer matches the deployment's ATLAN_APPLICATION_NAME.

The queue template is matched and replaced as a unit rather than token-filled.
Filling in place is what produced the divergence: with ATLAN_DEPLOYMENT_NAME
unset the worker drops the prefix and polls a bare `<app>`, while token-filling
yields `atlan-<app>-local`.

The unset case stays loud. constants.APPLICATION_NAME's "default" is scoped to
identity uses (object-store prefixes, log tagging) and excluded from queue
naming: `atlan-default-prod` reads as a legitimate queue, is polled by nobody,
and reproduces the original hang, whereas a literal `{app_name}` in the DAG is
greppable and diagnosable in one step. An unbaked token that the SDK can fill
from the registered app name is filled and logged at WARNING; with nothing to
fill it from, the token is served as-is and logged at ERROR.

Behaviour preserved deliberately:
- `{ClassName}-queue` when no app name exists — it predates the env-var
  convention and is load-bearing for local dev.
- DAG nodes dispatching to another app's queue (`atlan-publish-{deployment_name}`)
  are token-filled and otherwise left alone; they are legitimately not this
  app's queue.
- The `{deployment_name}` token's existing fallback for non-queue uses.

The e2e agent_spec mirror and the handler's own `{app_name}-queue` default now
route through the same helper, so neither is a fresh implementation of the rule.

FND-195
@linear

linear Bot commented Aug 10, 2026

Copy link
Copy Markdown

FND-195

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📜 Docstring Coverage Report

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

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/task_queue.py                                                         |    10 |    0 |    10 |   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                                                           |    64 |   22 |    42 |    66% |
| application_sdk/handler/service_errors.py                                                    |     4 |    0 |     4 |   100% |
| application_sdk/infrastructure/__init__.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_secret_utils.py                                              |     2 |    0 |     2 |   100% |
| application_sdk/infrastructure/bindings.py                                                   |    16 |    3 |    13 |    81% |
| application_sdk/infrastructure/capacity.py                                                   |    11 |    0 |    11 |   100% |
| application_sdk/infrastructure/context.py                                                    |     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% |
|

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-10T20:36:55.139929541Z . 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-10T20:37:08.907224405Z . repository

Report Summary

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

Scan Result Details

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

@atlan-app-fleet

atlan-app-fleet Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

☂️ Code Coverage

current status: ✅

Overall Coverage

Statements Covered Coverage Threshold Status
20976 19219 92% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: ec90bc7 by action🐍

The three manifest-serving branches were left with two behaviours: the
per-entrypoint and root-disk paths route through
_resolve_manifest_placeholders, while a programmatic AppManifest was
serialised and served verbatim. That is the drift FND-195 exists to
remove, reintroduced one branch over.

The programmatic branch is the shape that needs it most, not least: its
DAG is hand-built in Python, so there is no contract-toolkit bake behind
it at all and every "the toolkit already resolved this" argument is
inapplicable. create_app_handler_service(manifest=...) is public API, so
this is a reachable path, and an unresolved queue template there lands
in AE exactly as it would from disk.

Also records why a runtime fill exists alongside the toolkit's bake, in
the task_queue module docstring: #2270 proposed it, #2271 superseded it
by moving the fix into App.pkl, #2478 extended that to NativeApp.pkl.
The bakes are correct; what they cannot reach is adoption lag (apps with
already-committed manifests that have not regenerated) and writers
outside the toolkit's generation step (Heracles, native-migration-app,
atlan-local-marketplace-app's install-time DAG rewrite). Without that
context the module reads as a second mechanism competing with the
toolkit, and the next reader re-opens a settled question.

Refs FND-195. Supersedes the substitution in #3097, which used
constants.APPLICATION_NAME and so manufactured "default" for the unset
case.
@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

@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:24:11.236Z.

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 $0.8641230000000001, duration 16m 25s.
Error: unknown — {"type":"error","message":"ReadableStream received over RPC disconnected prematurely."}

Comment thread application_sdk/common/task_queue.py Outdated
Comment thread application_sdk/common/task_queue.py Outdated
@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Review (mothership): PR #3101 — fix(task-queue): derive the queue name once, and stamp it into the served manifest

Verdict: NEEDS FIXES

The design is right: one derivation, and the manifest side stamps the handler-configured queue instead of re-deriving it — that is the structural fix for the CONNECT-183 silent-hang class, and the module docstring's reasoning (adoption lag, non-toolkit writers, loud unset case) is exactly the analysis this bug deserved. But the implementation's core mechanism — whole-manifest byte substitution — under-delivers on the PR's own central invariant in two confirmed ways: the stamped queue can itself be corrupted by the later token passes, and the queue rewrite can hit DAG nodes and strings it doesn't own. Both are one class ("unscoped byte substitution") with one fix site. Tests pass (23 new + 4 handler-level); coverage of the divergence table is genuinely good.


Findings

application_sdk/common/task_queue.py

  • Important [BUG] L252-L255 — class: unscoped byte substitution. The residual {app_name} / {deployment_name} passes run over the entire manifest after the queue stamp, so a configured queue value containing literal token text (e.g. custom-{deployment_name}-queue via task_queue= / ATLAN_TASK_QUEUE) is mutated post-stamp; the served manifest then advertises a queue no worker polls while resolution.task_queue reports the original. Path: immediate fix — sentinel-protect the stamped queue during residual substitution (or substitute residuals first, stamp last) + regression test.
  • Important [BUG] L245-L249 — class: unscoped byte substitution. The queue-template rewrite matches atlan-{candidate}-{deployment_name} anywhere in the bytes, not just in this app's task_queue fields: a foreign-app DAG node whose baked queue matches a candidate (the candidates include both registered and env app names), or a description/metadata string containing the template text, gets re-pointed at this app's worker queue. Current tests are single-node only. Path: design decision — parse and rewrite only own-node task_queue fields, or scope the byte rewrite and document/test the accepted blast radius.
  • Nit [TEST] tests/unit/common/test_task_queue.py L122 — the worker↔manifest equality assertions share one oracle (_derive_task_queue delegates to the same task_queue_from_env the resolver falls back to), so both sides can drift together and still pass. The hard-coded expectation tests cover the simple rows; the precedence/override rows rely on the coupled oracle. Path: optional cleanup — add independent expected-value assertions for each precedence case and a multi-node manifest test.

Holistic Recommendations

  • This PR treats the cause, not the symptom — keep that. The one class to fix before merge is the substitution mechanism, not the derivation logic: both Important findings dissolve if the queue stamp is made field-aware (or at minimum sentinel-protected and ordered last). A json.loads → targeted rewrite → json.dumps on an already-build-time-validated manifest is cheap at request time relative to the disk read the route already does; the "no parse round-trip" note in the docstring is a performance argument, and it is buying correctness risk in the one place the PR is trying to eliminate it.

Strengths

  • The root-cause writeup (worker/manifest divergence, why the toolkit bake can't reach non-toolkit writers, why "default" must never feed queue naming) is model incident-review documentation — the next person to touch this code cannot reintroduce the bug without contradicting the docstring.
  • Tests are written as equality between the two paths for the FND-195 divergence rows — the right shape for a two-sides-must-agree bug.
  • The loud-unset case (serve the literal {app_name}, log at ERROR, never invent atlan-default-prod) is the correct operational posture and well covered.
  • deployment_fallback is explicitly excluded from queue derivation — the manufactured segment that caused the original divergence can't leak back in.

CI: no checks reported at review time
Models: Claude Opus 4.8 (3 domain agents: correctness, quality, structure) + GPT-5.3-codex (adversarial)
Cross-model agreement: 2/5 confirmed by both (2 dropped on DISAGREE after verification, 1 downgraded to test-note)
Run: view workflow logs + cost

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🤖 SDK Resolve — converged (round 3). The latest re-review on head ec90bc76 returned verdict READY_TO_MERGE with zero findings. The round-2 nit (malformed-manifest log wording) is fixed in ec90bc76. CI is green on all required checks. Merge-ready — handed to a human.

cmgrote and others added 2 commits August 10, 2026 18:49
…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.
…titution

The whole-manifest byte rewrite in resolve_manifest_tokens under-delivered on
the PR's own invariant in two ways, both the same class (unscoped byte
substitution):

* the residual {app_name}/{deployment_name} passes ran over the whole manifest
  after the queue stamp, so a configured queue containing literal token text
  (custom-{deployment_name}-queue via ATLAN_TASK_QUEUE) was mutated post-stamp
  into a queue no worker polls while resolution.task_queue reported the
  original;
* the queue-template rewrite matched atlan-{candidate}-{deployment_name}
  anywhere in the bytes, so a foreign-app DAG node whose baked queue matched a
  candidate, or a description string holding the template text, was re-pointed
  at this app's worker queue.

Parse the manifest and rewrite only values the manifest itself labels
task_queue, at any depth; fill the residual tokens in every other string in the
same walk so the fills never touch an already-stamped queue. A configured queue
is now stamped verbatim, and a manifest too malformed to parse falls back to
the pre-FND-195 byte behaviour with the stamp ordered before the fills.

Also pin each precedence/override row to an independent hard-coded expectation
rather than the coupled worker/manifest oracle, and cover the multi-node and
any-depth shapes the single-node tests missed.
@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:52:27.574Z.

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 $46.58697199999997, duration 29m 48s.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@sdk-review

Comment thread application_sdk/common/task_queue.py Outdated
@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Re-review (mothership): PR #3101 — fix(task-queue): derive the queue name once, and stamp it into the served manifest

Verdict: NEEDS FIXES

The field-aware rewrite resolves both Important findings from the previous round on the parseable-manifest path: the stamp now owns only values the manifest labels task_queue, and residual fills skip a stamped field, so a token-carrying override is served verbatim and a foreign node or description is never re-pointed. The independent-oracle test class resolves the shared-oracle nit. One residual hole remains, and it is the same defect class: the malformed-manifest fallback still does whole-manifest byte substitution, and its documented "stamp before fill" ordering does not protect a stamped queue that itself carries literal token text — verified live below. Because a build-time-validated manifest parses by construction, this path should be unreachable in the deployments the PR targets; but it is reachable on disk corruption or a hand-edited manifest, and the code + docstring claim a guarantee it does not deliver. Either close it or delete the path.


Delta from previous review

  • Resolved (3): the two Important "unscoped byte substitution" findings (post-stamp residual corruption; foreign-node/description re-pointing) — closed on the parsed path by the field-aware walk; the shared-oracle test nit — closed by TestPrecedenceIndependentOracle's hard-coded per-row expectations.
  • Still present (1): the byte-substitution class survives in the malformed-manifest fallback (see findings); the docstring's "stamp ordered before the residual fills so they cannot corrupt a stamped queue" claim is not borne out.
  • New (0) on the delta hunks.

Findings

application_sdk/common/task_queue.py

  • Important [BUG] L362-L367 — class: unscoped byte substitution. The malformed-manifest fallback still stamps by whole-manifest byte replace (_stamp_task_queue_bytes) and then runs the residual {app_name} / {deployment_name} byte replaces over the result. A configured queue that itself carries literal token text (custom-{deployment_name}-queue via ATLAN_TASK_QUEUE) is stamped and then mutated by the later pass — verified live: input {"task_queue": "atlan-dbt-{deployment_name}" (truncated → unparseable) with task_queue="custom-{deployment_name}-queue" serves {"task_queue": "custom-prod-queue" while resolution.task_queue still reports custom-{deployment_name}-queue. The same path also re-points foreign-node / description bytes (the exact blast radius the parsed path removes). The docstring's "the stamp ordered before the residual fills so they cannot corrupt a stamped queue" is not true of byte substitution — ordering does not scope the fill away from the inserted bytes. Reachability is narrow (build-time validation makes a malformed manifest exceptional), which is why this is Important rather than Critical — but the code and comment both claim a guarantee the path does not deliver. Path: design decision — preferred: on json.loads failure, serve the raw bytes unstamped and log at ERROR (a manifest the build-time validation never saw is already broken; a loud failure beats a silently wrong queue). Alternative: sentinel-protect the stamped bytes across the residual replaces. Either way, correct the ordering claim in the docstring and add a regression test for a token-carrying override through the fallback.

Holistic Recommendations

  • The structural fix is the right one and the parsed path now delivers it. The single remaining question is what to do with a manifest that cannot be parsed: today the PR keeps the pre-FND-195 byte behaviour as a "degraded, never dropped" fallback, which re-imports the very defect class being removed. The cleaner structural answer is to fail loud on input that cannot be parsed — that input only exists outside the toolkit's guarantee anyway — and delete the dual mechanism rather than maintain two paths with different ownership semantics.

Strengths

  • Field-aware stamping is a genuine root-cause fix: one derivation, stamped from the handler-configured queue, residual fills structurally unable to touch a stamped field.
  • TestPrecedenceIndependentOracle replaces the shared oracle with hard-coded per-row expectations, so worker and manifest can no longer drift together undetected.
  • TestQueueStampIsFieldAware covers the override-with-token-text, foreign-node, description, nested-depth, and malformed-fallback shapes.
  • The hook-output second reconciliation pass closes a real bypass: a compute_manifest hook replaces the manifest wholesale, and its emitted DAG was previously served unreconciled.
  • Docstring reasoning (adoption lag, non-toolkit writers, loud unset case) is thorough and honest about the reconciliation point's scope.

CI: all passing
Models: claude-opus (review) + adversarial skipped (over 70% time budget)
Cross-model agreement: n/a — Wave 2 skipped per budget rule; all Wave 1 findings >= 0.80 kept, Important finding verified live by the reviewer
Run: view workflow logs + cost

@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-10T18:22:55.066Z.

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 $0.923104, duration 4m 5s.

@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

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

…bstitution

A manifest too malformed to parse fell back to whole-manifest byte
substitution: stamp the queue template, then run the residual {app_name} /
{deployment_name} replaces over the result. The stamp could not be scoped
away from those fills, so a configured queue carrying literal token text
(custom-{deployment_name}-queue via ATLAN_TASK_QUEUE) was stamped and then
mutated by the later pass while resolution.task_queue still reported the
original — the served manifest and the worker disagreed on the queue. The
same path also re-pointed foreign-node and description bytes.

Serve the malformed manifest back unstamped and log at ERROR instead. Such
a manifest is one the build-time validation never saw — already broken, and
a loud failure beats a silently wrong queue. Delete _stamp_task_queue_bytes
and correct the docstring's ordering claim, and add a regression test for a
token-carrying override through the fallback.
cmgrote added a commit that referenced this pull request Aug 10, 2026
O005 fired five times on the module that implements the behaviour it is
meant to protect. Run against PR #3101 (FND-195):

  common/task_queue.py:86   APP_NAME_TOKEN = "{app_name}"
  common/task_queue.py:239  attribute docstring naming the token
  common/task_queue.py:243  attribute docstring naming the token
  handler/service.py:2009   logger.error(... unresolved {app_name} ...)
  handler/service.py:2023   logger.warning(... unbaked {app_name} ...)

None can freeze into an identifier. A rule that flags the canonical fix,
the docs describing it, and the logs diagnosing it is a rule people
suppress — which costs the true positives too.

Detection now anchors on the token reaching a value. Three exclusions
added, each narrow:

* documentation — the value of any bare string expression statement.
  The previous check excluded only body[0] of Module/ClassDef/FunctionDef,
  so a PEP 257 attribute docstring (a bare string after a field
  annotation) was flagged. A string bound to nothing cannot be
  dispatched.
* diagnostic text — inside a logging call, warnings.warn, or a raise.
  Reporting an unresolved token requires quoting it.
* token sentinels and message constants — bound to an ALL_CAPS name
  where the literal is exactly the token (its own definition) or the
  name reads as prose (_MESSAGE, RATIONALE).

Kept narrow deliberately: TASK_QUEUE = "atlan-{app_name}-prod" is
ALL_CAPS but neither bare-token nor prose-named, so it still fires. New
tests pin that, plus keyword arguments, values at any depth in a DAG
literal, a returned template, and a bare token bound to a lowercase name
— the shapes an over-broad exclusion would have swallowed. All ten
original tests pass unchanged.

Also realigned the rule metadata, which predated FND-195 and claimed no
canonical helper exists. application_sdk.common.task_queue now provides
derive_task_queue and resolve_manifest_tokens, so remediation has one
target. Detection stays shape-anchored rather than import-anchored on
purpose: the writers most worth catching are hand-authored templates
outside the SDK that import nothing at all.

The checker's own _MESSAGE is now built from _TOKEN rather than spelling
the token inline, so this module does not depend on its own exclusions to
avoid self-flagging.

Rule docs regenerated. Note for a follow-up, not addressed here: O005 is
claimed by both this PR and #3089 (DirectRocksdictImport), both targeting
0.18.0 — whichever merges second needs renumbering, including the
{#o005} docs anchor. Deciding which yields is the two authors' call.
@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-10T20:22:51.442Z.

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 $5.497155, duration 9m 9s.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Re-review (mothership): PR #3101 — fix(task-queue): derive the queue name once, and stamp it into the served manifest

Verdict: READY TO MERGE

The single remaining Important finding from the previous round is closed at the root: the malformed-manifest byte-substitution fallback — the last place the unscoped-substitution class lived — is deleted, not patched. An unparseable manifest is now served byte-for-byte unstamped and logged at ERROR, so no residual fill can ever mutate a stamped queue, and the code finally delivers what the docstring claims. The author took the recommended path (fail loud, delete the dual mechanism) rather than the sentinel-protect alternative, and added a dedicated regression test for the exact divergence the round-2 review demonstrated live. One actionable nit remains on the new branch's log wording; nothing blocks.


Delta from previous review

  • Resolved (1): the Important "unscoped byte substitution" finding on the malformed-manifest fallback — _stamp_task_queue_bytes and the residual byte fills on that path are gone; a token-carrying override (custom-{deployment_name}-queue) now survives untouched, covered by test_unparseable_manifest_with_token_carrying_override_is_not_mutated.
  • Still present (0).
  • New (1): one Nit on the new fallback branch's log wording (see findings).
  • Downgraded (0).

Findings

application_sdk/common/task_queue.py

  • Nit [DX] L368-L374 — class: diagnostic accuracy. The fallback branch runs both when json.loads raises and when parsing succeeds with a scalar root (null, a number, a string — only dict/list enter the rewrite path), but the ERROR always says the manifest "does not parse as JSON", which is inaccurate for valid JSON with a non-container root and will send an operator hunting a syntax error that isn't there. Path: optional cleanup — distinguish the two cases in the log message (e.g. "does not parse as a JSON object/array" or track the parse failure separately); alternatively document that scalar roots are treated as malformed by design.

Holistic Recommendations

  • None. This round converges: with the dual mechanism deleted, there is exactly one stamping path (the field-aware walk) and one loud-failure path, and their ownership semantics are the same. Nothing further to generalize.

Strengths

  • The author chose the structurally clean option from the round-2 path-forward — delete the byte fallback rather than sentinel-protect it — so the defect class is unrepresentable, not merely guarded.
  • test_unparseable_manifest_with_token_carrying_override_is_not_mutated pins the exact divergence the previous review verified live (served bytes vs resolution.task_queue), with an independent hard-coded expectation.
  • The new ERROR message names the failure, the reason byte substitution was rejected, and the operator action ("regenerate or restore it") — a model actionable diagnostic.
  • Docstrings on resolve_manifest_tokens and _rewrite_task_queue_fields were updated in the same commit, so no stale "byte substitution" claims survive anywhere in the module.

CI: all passing
Models: claude-opus (review) + gpt-5.3-codex (adversarial)
Cross-model agreement: 1/1 finding confirmed by both; 2 GPT-only candidates dropped below the 0.90 blind-spot confidence floor (0.84, 0.71)
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.

…path log

The malformed-manifest fallback runs both when json.loads raises and when
parsing succeeds with a scalar root (null, a number, a string), but the
ERROR always said the manifest "does not parse as JSON" — inaccurate for
valid JSON with a non-container root and liable to send an operator hunting
a syntax error that isn't there. Track the parse failure separately and say
"scalar root (no object to walk)" for the well-formed-but-unwalkable case.
@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-10T20:52:55.885Z.

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 $6.489702000000001, duration 10m 22s.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Re-review (mothership): PR #3101 — fix(task-queue): derive the queue name once, and stamp it into the served manifest

Verdict: READY TO MERGE

The single actionable nit from the previous round is resolved at the root: the malformed-manifest ERROR now distinguishes a genuine parse failure ("does not parse as JSON") from valid JSON with a scalar root ("parses as JSON but has a scalar root (no object to walk)"), so an operator is no longer sent hunting a syntax error that isn't there. The delta is one focused commit (+20/-10 source, +19/-1 test) with a dedicated regression test pinning both branches of the log, and the cross-model adversarial pass confirmed the fix is sound with no new defects. Nothing blocks.


Delta from previous review

  • Resolved (1): the Nit on the malformed-manifest log wording (class: diagnostic accuracy) — the fallback branch now tracks parse_error separately and emits an accurate reason for each case, covered by the new test_scalar_root_manifest_is_served_unstamped_with_accurate_log and the updated assertion in test_unparseable_manifest_is_served_unstamped_and_logged (which now renders the %-args before asserting, so the format string can't drift from the test).
  • Still present (0).
  • New (0).
  • Downgraded (0).

Findings

No findings. The delta is clean: control flow is mutually exclusive, scalar roots (null, bools, numbers, strings) all land on the correct branch, empty bytes raise JSONDecodeError (→ "does not parse as JSON"), the log interpolates only a static reason string (no manifest content, no format-specifier injection), and no Temporal contract or determinism surface is touched.

Holistic Recommendations

  • None. This round converges. The one candidate the adversarial pass raised — rewording the trailing "malformed on disk" sentence for the scalar-root case, and a test-hardening assertion on the guidance text — are new optional nits on hunks that were reviewable last round and drew no nit; under the re-review convergence rules they are not raised as findings. Both are cosmetic and can be picked up any time the file is next touched for a substantive reason.

Strengths

  • The author took the nit's recommended path exactly: distinguish the two cases in the log message rather than documenting the inaccuracy, and pinned both branches with tests in the same commit.
  • The new test's negative assertion ("does not parse as JSON" not in message) is the guard that keeps the two branches from silently merging again in a future edit.
  • The test-side switch to mock_error.call_args.args[0] % mock_error.call_args.args[1:] renders the log args before asserting, so the test verifies the operator-visible message, not the format template — a strictly stronger assertion than the round-2 version.

CI: all passing
Models: claude-opus (review) + gpt-5.3-codex (adversarial)
Cross-model agreement: nit resolution confirmed by both (AGREE, 98%); 2 GPT candidates withdrawn under the re-review convergence rules (new optional nits on previously-reviewed hunks, 0.78 and 0.71 confidence)
Run: view workflow logs + cost

mothership-ai Bot added a commit that referenced this pull request Aug 10, 2026
…ediation target

Addresses the round-1 review on #3094 (4 findings):

* Escaped-brace f-strings (f"{{app_name}}") are no longer exempt: the
  braces are not interpolated, so the runtime value carries the literal
  token — the exact frozen shape the rule exists to catch. The JoinedStr
  waiver is line-keyed (positions survive the SARIF emitter's re-parse);
  a docstring or diagnostic f-string on the same line stays exempt, and
  a resolving f-string quoting the token in a log call is never re-flagged.
* The prose-name exclusion now matches the trailing delimited segment
  (_MESSAGE, START_MESSAGE, VALIDATION_RATIONALE) instead of any
  substring, so queue templates whose names merely contain a prose
  fragment (MESSAGE_QUEUE, HELP_QUEUE, DOC_QUEUE) fire again.
* The runtime SARIF message and the rule rationale/description no longer
  send users to application_sdk.common.task_queue as if it existed on
  main — that helper ships with FND-195 (#3101, unmerged). The message
  now leads with the always-available fixes (f-string /
  .format(app_name=...)) and names the helper as the canonical target
  only once the FND-195 release lands.
* optimizations.prose.md gains the O005 fix prescription (the generic
  O-series remediation dispatch routes O005 here): in-scope resolution,
  threading from a caller, the version-gated helper, and when a
  cross-file-resolution suppression is legitimate.

Dogfood under the gate's exclude scope is byte-identical to the reviewed
HEAD (13 known testing/ hits, all WARN-tier). 28 O005 tests pass,
including new regressions for each finding.
@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

✅ SDK Resolve — merge-ready

@cmgrote — your turn. This PR is merge-ready; a human merges (I never do).

No reviewer list is configured (vars.SDK_RESOLVE_REVIEWERS is unset), so a human assigns the reviewer — I requested review from you as the requester.

Outcome: green CI + zero @sdk-review findings (nits included) + verdict READY_TO_MERGE.

  • Rounds: 3
  • Findings fixed: 1 — the round-2 Nit on the malformed-manifest log wording (class: diagnostic accuracy). Fixed in ec90bc76: the fallback now tracks the parse failure separately and logs an accurate reason for each case — "does not parse as JSON" for a genuine parse failure vs "parses as JSON but has a scalar root (no object to walk)" for valid JSON with a non-container root — with a regression test pinning both branches (test_scalar_root_manifest_is_served_unstamped_with_accurate_log) and a stronger assertion in the existing unparseable-manifest test.
  • Findings dismissed: 0
  • Final CI: all required checks passing.
  • Final verdict: READY_TO_MERGE (re-review on head ec90bc76).

Nothing remains open. The reviewer's two optional cosmetic candidates (rewording a trailing sentence for the scalar-root case; a test-hardening assertion) were explicitly withdrawn under the re-review convergence rules and are not findings — safe to pick up any future time the file is touched.

@cmgrote
cmgrote enabled auto-merge August 10, 2026 21:09
@cmgrote

cmgrote commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

@sdk-review

@cmgrote
cmgrote disabled auto-merge August 10, 2026 21:10
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🔍 SDK Review (mothership) triggered by @cmgrote at 2026-08-10T21:11:22.351Z.

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.428033, duration 5m 21s.

@mothership-ai

mothership-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

SDK Re-review (mothership): PR #3101 — fix(task-queue): derive the queue name once, and stamp it into the served manifest

Verdict: READY TO MERGE

This is a human re-trigger on the identical HEAD the previous round already approved (ec90bc7). The delta since that review is empty (0 changed lines), so this pass is a verification-only re-review: no new hunks means no new findings can exist, and the prior round closed with zero findings. The scalar-root manifest fix that round approved is still intact at this HEAD, and all CI legs are green. Nothing blocks.


Delta from previous review

  • Resolved (0).
  • Still present (0).
  • New (0).
  • Downgraded (0).

No code changed since the prior review of this same HEAD (<!-- REVIEWED_HEAD: ec90bc76... --> → current HEAD ec90bc7), so there is nothing to re-derive. The prior round's zero-finding verdict stands.

Findings

No findings. The diff is byte-identical to the one previously reviewed and approved: the malformed-manifest ERROR distinguishes a genuine parse failure ("does not parse as JSON") from valid JSON with a scalar root ("parses as JSON but has a scalar root (no object to walk)"), both branches are pinned by regression tests, and no Temporal contract or determinism surface is touched.

Holistic Recommendations

  • None. The prior round converged and this HEAD is unchanged.

Strengths

  • The branch has stayed clean across review rounds — the approved fix is untouched and CI remains green.

CI: all passing
Models: claude-opus (verification-only re-review; adversarial skipped — empty delta on an already-approved HEAD)
Cross-model agreement: carried from prior round (nit resolution confirmed by both models, AGREE 98%)
Run: view workflow logs + cost

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

@cmgrote
cmgrote added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit b1672ae Aug 10, 2026
65 checks passed
@cmgrote
cmgrote deleted the chrishehim/fnd-195 branch August 10, 2026 21:31
mothership-ai Bot pushed a commit that referenced this pull request Aug 10, 2026
O005 fired five times on the module that implements the behaviour it is
meant to protect. Run against PR #3101 (FND-195):

  common/task_queue.py:86   APP_NAME_TOKEN = "{app_name}"
  common/task_queue.py:239  attribute docstring naming the token
  common/task_queue.py:243  attribute docstring naming the token
  handler/service.py:2009   logger.error(... unresolved {app_name} ...)
  handler/service.py:2023   logger.warning(... unbaked {app_name} ...)

None can freeze into an identifier. A rule that flags the canonical fix,
the docs describing it, and the logs diagnosing it is a rule people
suppress — which costs the true positives too.

Detection now anchors on the token reaching a value. Three exclusions
added, each narrow:

* documentation — the value of any bare string expression statement.
  The previous check excluded only body[0] of Module/ClassDef/FunctionDef,
  so a PEP 257 attribute docstring (a bare string after a field
  annotation) was flagged. A string bound to nothing cannot be
  dispatched.
* diagnostic text — inside a logging call, warnings.warn, or a raise.
  Reporting an unresolved token requires quoting it.
* token sentinels and message constants — bound to an ALL_CAPS name
  where the literal is exactly the token (its own definition) or the
  name reads as prose (_MESSAGE, RATIONALE).

Kept narrow deliberately: TASK_QUEUE = "atlan-{app_name}-prod" is
ALL_CAPS but neither bare-token nor prose-named, so it still fires. New
tests pin that, plus keyword arguments, values at any depth in a DAG
literal, a returned template, and a bare token bound to a lowercase name
— the shapes an over-broad exclusion would have swallowed. All ten
original tests pass unchanged.

Also realigned the rule metadata, which predated FND-195 and claimed no
canonical helper exists. application_sdk.common.task_queue now provides
derive_task_queue and resolve_manifest_tokens, so remediation has one
target. Detection stays shape-anchored rather than import-anchored on
purpose: the writers most worth catching are hand-authored templates
outside the SDK that import nothing at all.

The checker's own _MESSAGE is now built from _TOKEN rather than spelling
the token inline, so this module does not depend on its own exclusions to
avoid self-flagging.

Rule docs regenerated. Note for a follow-up, not addressed here: O005 is
claimed by both this PR and #3089 (DirectRocksdictImport), both targeting
0.18.0 — whichever merges second needs renumbering, including the
{#o005} docs anchor. Deciding which yields is the two authors' call.
mothership-ai Bot added a commit that referenced this pull request Aug 10, 2026
…ediation target

Addresses the round-1 review on #3094 (4 findings):

* Escaped-brace f-strings (f"{{app_name}}") are no longer exempt: the
  braces are not interpolated, so the runtime value carries the literal
  token — the exact frozen shape the rule exists to catch. The JoinedStr
  waiver is line-keyed (positions survive the SARIF emitter's re-parse);
  a docstring or diagnostic f-string on the same line stays exempt, and
  a resolving f-string quoting the token in a log call is never re-flagged.
* The prose-name exclusion now matches the trailing delimited segment
  (_MESSAGE, START_MESSAGE, VALIDATION_RATIONALE) instead of any
  substring, so queue templates whose names merely contain a prose
  fragment (MESSAGE_QUEUE, HELP_QUEUE, DOC_QUEUE) fire again.
* The runtime SARIF message and the rule rationale/description no longer
  send users to application_sdk.common.task_queue as if it existed on
  main — that helper ships with FND-195 (#3101, unmerged). The message
  now leads with the always-available fixes (f-string /
  .format(app_name=...)) and names the helper as the canonical target
  only once the FND-195 release lands.
* optimizations.prose.md gains the O005 fix prescription (the generic
  O-series remediation dispatch routes O005 here): in-scope resolution,
  threading from a caller, the version-gated helper, and when a
  cross-file-resolution suppression is legitimate.

Dogfood under the gate's exclude scope is byte-identical to the reviewed
HEAD (13 known testing/ hits, all WARN-tier). 28 O005 tests pass,
including new regressions for each finding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants