feat(conformance): add O005 UnresolvedAppNamePlaceholder - #3094
Conversation
|
@sdk-resolve please review this PR, handle any review nuances/fixes, and get CI green. |
|
🤖 SDK Resolve started. Driving this PR toward merge-ready — fixing CI + every This runs out-of-band and can take several minutes; I'll comment here when it finishes. |
|
@sdk-review |
Earlier @sdk-review trigger (click to expand)🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T15:08:21.325Z. Watch the workflow run live — the review summary will appear as a separate comment when complete (typical: 5–30 min, hard cap 2h). 🟥 Run ended without a clean completion — status |
…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.
O005 is claimed by UnresolvedAppNamePlaceholder (#3094), which will merge first. Renumber this rule to O006 so the two land without an ID collision.
|
Pushed The problemRun against #3101's head, O005 produced five findings:
None of those can freeze into an identifier. Two are general classes rather than quirks of that PR: attribute docstrings aren't That combination is the failure mode worth avoiding — a rule that flags the canonical fix, the docs describing it, and the logs diagnosing it gets suppressed wholesale, and the true positives go with it. What changedDetection now anchors on the token reaching a value. Three exclusions, each deliberately narrow:
The narrowness is the part I'd most like you to check. Metadata realignmentThe rationale and I kept detection shape-anchored anyway, and made that explicit: the writers most worth catching are hand-authored templates outside the SDK that import nothing at all. So your original instinct holds — it just needed a different justification now that the helper exists. Also: the checker's Verification
One thing I did not decide for youO005 is claimed by both this PR and #3089 ( Separately, and my error rather than yours: I'd written elsewhere that O005 guards a hook hardcoding a concrete-but-wrong queue ( |
|
@sdk-resolve |
|
🤖 SDK Resolve started. Driving this PR toward merge-ready — fixing CI + every This runs out-of-band and can take several minutes; I'll comment here when it finishes. |
|
@sdk-review |
Earlier @sdk-review trigger (click to expand)🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T20:27:43.511Z. 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 |
SDK Review (mothership): PR #3094 — feat(conformance): add O005 UnresolvedAppNamePlaceholderVerdict: NEEDS FIXES
Findings
Class note (one root cause, several siblings)Findings 1–3 of the Holistic Recommendations
Strengths
CI: all passing (sdk-review pending = this run) |
|
🤖 SDK Resolve — round 4. Picked up the latest review: 1 open finding(s) (1 blocking, 0 nit). Fixing it now, then I'll push and re-run |
…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.
|
@sdk-review |
Earlier @sdk-review trigger (click to expand)🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T21:03:12.789Z. 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 |
SDK Re-review (mothership): PR #3094 — feat(conformance): add O005 UnresolvedAppNamePlaceholderVerdict: READY TO MERGE
Delta from previous review
Findings
Holistic Recommendations
Strengths
CI: all passing (sdk-review pending = this run; Endor Labs queued) |
atlan-ci
left a comment
There was a problem hiding this comment.
SDK reviewer's verdict: READY TO MERGE.
Full review summary is in the comment posted on this PR.
|
@sdk-review |
Earlier @sdk-review trigger (click to expand)🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T21:33:27.909Z. 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 |
SDK Re-review (mothership): PR #3094 — feat(conformance): add O005 UnresolvedAppNamePlaceholderVerdict: READY TO MERGE
Delta from previous review
Findings
Strengths
CI: all passing (sdk-review pending = this run; Endor Labs pending) |
atlan-ci
left a comment
There was a problem hiding this comment.
SDK reviewer's verdict: READY TO MERGE.
Full review summary is in the comment posted on this PR.
|
@sdk-review |
Earlier @sdk-review trigger (click to expand)🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T21:44:30.642Z. 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 |
SDK Re-review (mothership): PR #3094 — feat(conformance): add O005 UnresolvedAppNamePlaceholderVerdict: NEEDS REBASE
Delta from previous review
FindingsPR metadata
Strengths
CI: all passing (Endor Labs, Socket green; conformance legs not yet run on this HEAD; sdk-review pending = this run) |
Flags a plain string literal still carrying an unsubstituted '{app_name}'
token — the shape that shipped a task queue no worker polls and hung
dbt:process to its 24h heartbeat backstop (CONNECT-183).
The substitution this needs has been independently hand-rolled at least
four times across separate codebases (Heracles, native-migration-app,
atlan-local-marketplace-app #539/CONNECT-191, atlan-hightouch-app
ARUN-1039) because no shared, discoverable utility exists yet in the SDK.
Detection is shape-anchored rather than import-anchored for that reason —
it flags the unresolved literal directly instead of the absence of a
canonical helper call.
WARN tier, not autofixable (the correct fix depends on where app_name is
actually available in scope), suppressible via
`# conformance: ignore[O005] <reason>`.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
…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.
…s (O005 nit)
Round-2 review nit: the line-keyed waiver over-reported when a resolved
or diagnostic literal shared a physical line with an escaped-brace
f-string — `x="atlan-{app_name}".format(app_name=a); y=f"{{app_name}}"`
reported two findings (the already-resolved receiver was a false
positive), and `f"{{app_name}}".format(app_name=a)` was flagged despite
resolving at runtime.
The waiver is now node-keyed on the escaped-brace f-string's own
token-bearing Constant pieces, collected by object identity in a single
walk (an AST node's id is only stable while the object is alive, so an
id-set built across sequential walks can be silently corrupted by id
recycling). A piece another exclusion independently covers — a docstring
or diagnostic f-string piece, or a .format(app_name=...) receiver —
keeps its exemption, and a f-string receiver of a resolving .format call
is exempted alongside plain-literal receivers.
Regression tests pin all three nit shapes; the full O005 matrix (31
tests) and the 2186-test conformance suite pass.
…e-flag rework The _diagnostic_constants docstring referenced the line-based waiver that bb4f4a3 replaced with node-identity keying; point it at the re-flag pass instead (round-3 review nit).
Regenerated via gen-rule-docs so the catalog page carries both O005 (this PR) and O006 (#3089, landed on main) after the rebase.
c3dbdae to
1e05c7b
Compare
|
@sdk-review |
|
🔍 SDK Review (mothership) triggered by @mothership-ai[bot] at 2026-08-10T21:59:22.787Z. 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 |
SDK Re-review (mothership): PR #3094 — feat(conformance): add O005 UnresolvedAppNamePlaceholderVerdict: READY TO MERGE
Delta from previous review
FindingsNone. Strengths
CI: all passing (Endor Labs, Socket, CodeQL, Pre-commit, and every Conformance leg — Optimizations, Suite unit tests, Conformance Gate — green on this HEAD; sdk-review pending = this run) |
atlan-ci
left a comment
There was a problem hiding this comment.
SDK reviewer's verdict: READY TO MERGE.
Full review summary is in the comment posted on this PR.
|
🤖 SDK Resolve — final report. @cmgrote this PR is merge-ready — handing back to a human for the merge call. Outcome: MERGE-READY — green required CI + zero Note on reviewers: NO reviewer list is configured for this run ( Rounds: 5 | Findings fixed: 7 | Dismissed: 0
Round 1 (4 findings):
Round 2 (1 nit): the line-keyed re-flag over-reported exempt constants sharing a physical line with an escaped-brace f-string (a resolved Round 3 (1 nit): stale docstring sentence referencing the removed line-based waiver — reworded. Round 4 (1 struct): O006 DirectRocksdictImport (#3089) landed on Final CI: all checks pass (no failing, none pending) on |
Summary
UnresolvedAppNamePlaceholderto the O-series conformance checks: flags a plain string literal that still carries an unsubstituted{app_name}token (not an f-string, not a.format(app_name=...)receiver, not a docstring)."atlan-{app_name}-production"meant no worker ever polled it, hangingdbt:processto its 24h heartbeat backstop before failing.substitute_app_name_placeholder) has been independently hand-rolled at least 4 times across separate codebases — Heracles (Go), native-migration-app,atlan-local-marketplace-app(CONNECT-191, #539), andatlan-hightouch-app(ARUN-1039) — because no shared, discoverable utility exists in the SDK yet. Detection here is deliberately shape-anchored, not import-anchored: it flags the unresolved literal directly rather than checking for a canonical helper call that doesn't exist yet.WARNtier, not autofixable (the correct fix depends on whereapp_nameis actually available in scope — sometimes an f-string is right, sometimes the value needs threading in from a caller), suppressible via# conformance: ignore[O005] <reason>.Ref: FND-184 (support-patterns tracking ticket)
Test plan
tests/test_app_name_placeholder.py— fires on bare-literal assignment, call argument, dict value, and a.format()call missing theapp_namekwarg; silent on f-strings, resolving.format(app_name=...)calls, module/function docstrings, and literals with no token; suppressed by inline directivetest_catalog_o_series_presentupdated to include O005atlan-application-sdk-conformance gen-rule-docs(never hand-edited)uv run --with pytest pytest tests/ -q --ignore=tests/test_sdk_contract_mixins.py— 2162 passed, 1 pre-existing unrelated failure (test_app_name_alignment.py::test_sdk_base_names_matches_templates_all, needs a siblingapplication_sdkinstall this standalone checkout doesn't have — documented pre-existing gap, not touched by this change)pyproject.tomlversion andCHANGELOG.mddeliberately left untouched (release-automation owned)Not covered by this PR
substitute_app_name_placeholderSDK helper — that's a separate, larger design decision (where doesapp_namecome from at each call site) tracked as its own follow-up on FND-184.🤖 Generated with Claude Code