feat: auto_packaging toggle via Benchling App Config settings#395
Open
QuiltSimon wants to merge 2 commits into
Open
feat: auto_packaging toggle via Benchling App Config settings#395QuiltSimon wants to merge 2 commits into
QuiltSimon wants to merge 2 commits into
Conversation
Adds ["quilt", "settings", "auto_packaging"] to the Tier 1 App Config namespace. When false, entry and canvas lifecycle events render/refresh the canvas without enqueueing packaging work; scientists trigger packaging via the canvas Update Package button. Defaults to true (current behavior) when unset, and fails open if config lookup errors so a Benchling API hiccup never drops packaging work. Also fixes unknown canvas button interactions falling through to the packaging path (previously enqueued unintended work). Supersedes the Secrets-Manager-based auto_packaging from PR #393, reimplemented on top of the three-tier routing system per the platform-config-first direction discussed in #engineering. Co-authored-by: Cursor <cursoragent@cursor.com>
Enables immediate config cache invalidation when an operator toggles a ["quilt"] App Config Item in the Benchling UI. Without this subscription the configuration.updated lifecycle event is not delivered, so the routing cache relies entirely on the TTL (60s) for invalidation. Co-authored-by: Cursor <cursoragent@cursor.com>
2 tasks
Comment on lines
+717
to
+733
| def _resolve_auto_packaging(payload: Payload) -> bool: | ||
| """Resolve auto_packaging from Tier 1 App Config for this event. | ||
|
|
||
| Defaults to True (current behavior) when unset or when config lookup | ||
| fails, so a Benchling API hiccup never silently drops packaging work. | ||
| """ | ||
| assert benchling is not None and config is not None | ||
| try: | ||
| secrets = config.get_benchling_secrets() | ||
| return config.resolve_auto_packaging(benchling, secrets) | ||
| except Exception as exc: | ||
| logger.warning( | ||
| "Failed to resolve auto_packaging setting; defaulting to enabled", | ||
| error=str(exc), | ||
| error_type=type(exc).__name__, | ||
| ) | ||
| return True |
There was a problem hiding this comment.
The
payload parameter is declared but never read inside the function — all logic derives solely from config and benchling captured in the closure. Keeping a dead parameter is misleading: callers may assume the result is per-payload, and future authors may accidentally add per-payload logic while thinking the signature already supports it.
Suggested change
| def _resolve_auto_packaging(payload: Payload) -> bool: | |
| """Resolve auto_packaging from Tier 1 App Config for this event. | |
| Defaults to True (current behavior) when unset or when config lookup | |
| fails, so a Benchling API hiccup never silently drops packaging work. | |
| """ | |
| assert benchling is not None and config is not None | |
| try: | |
| secrets = config.get_benchling_secrets() | |
| return config.resolve_auto_packaging(benchling, secrets) | |
| except Exception as exc: | |
| logger.warning( | |
| "Failed to resolve auto_packaging setting; defaulting to enabled", | |
| error=str(exc), | |
| error_type=type(exc).__name__, | |
| ) | |
| return True | |
| def _resolve_auto_packaging() -> bool: | |
| """Resolve auto_packaging from Tier 1 App Config for this event. | |
| Defaults to True (current behavior) when unset or when config lookup | |
| fails, so a Benchling API hiccup never silently drops packaging work. | |
| """ | |
| assert benchling is not None and config is not None | |
| try: | |
| secrets = config.get_benchling_secrets() | |
| return config.resolve_auto_packaging(benchling, secrets) | |
| except Exception as exc: | |
| logger.warning( | |
| "Failed to resolve auto_packaging setting; defaulting to enabled", | |
| error=str(exc), | |
| error_type=type(exc).__name__, | |
| ) | |
| return True |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a runtime
auto_packagingtoggle to the three-tier routing system, configured via Benchling App Configuration Items rather than AWS Secrets Manager.What changes
["quilt", "settings", "auto_packaging"]— whenfalse, entry events (v2.entry.*) and canvas lifecycle events (v2.canvas.initialized,v2.canvas.created) no longer automatically enqueue packaging work. The canvas renders with the Update Package button so scientists trigger packaging per-entry.true: existing behavior is preserved when the setting is absent or unrecognized.v2-beta.app.configuration.updated, so toggling the value in the Benchling UI takes effect on the next event (no restart, no60s cache wait).Bugfix included
Unknown canvas button interactions now return an explicit
{"status": "ignored"}response instead of falling through to the packaging path. Previously, an unrecognizedbuttonIdwould enqueue unintended packaging work (withauto_packagingtrue) or silently re-render the canvas (withauto_packagingfalse).Files to focus your review on
docker/src/routing_config.pyRuntimeSettings.auto_packaging: bool | None,_bool_value()parserdocker/src/config.pyConfig.auto_packaging,resolve_auto_packaging()methoddocker/src/app.py_resolve_auto_packaging(),_render_canvas_without_packaging(), unknown-button early returndocker/app-manifest.yamlv2-beta.app.configuration.updateddocker/scripts/app_config.py["quilt", "settings", "auto_packaging"]as"true"docker/tests/test_routing_config.pydocker/tests/test_app.pyRelationship to PR #393
This supersedes the Secrets-Manager-based
auto_packagingfrom #393. That PR routed configuration throughBenchlingSecretDataandsync-secrets.ts; this PR follows the platform-config-first principle discussed in the #engineering thread: all configurable behavior lives in the platform (Benchling App Config), not in AWS console configuration. Closing #393 in favor of this PR.Test plan
docker/tests/test_routing_config.py:auto_packagingparses"true","False", unset, garbagedocker/tests/test_app.py: entry event returnsskippedwhenauto_packagingis offdocker/tests/test_app.py: canvas lifecycle renders without packaging when offdocker/tests/test_app.py: unknown button returnsignoredwithout enqueuing packaging414 passed, 37 deselectedbench.dev.quilttest.com, toggleauto_packagingvia Benchling UI, verify entry events no longer auto-package and Update Package button still worksMade with Cursor
Greptile Summary
Adds a runtime
auto_packagingtoggle controlled by the["quilt", "settings", "auto_packaging"]Benchling App Config Item, wired through the existing three-tier routing/cache system. When disabled, entry and canvas lifecycle events render the canvas without enqueueing SQS work; the Update Package button is always unaffected. Also fixes a pre-existing bug where unrecognised canvasbuttonIdvalues would fall through to the packaging path.routing_config.py: New_bool_value()parser andRuntimeSettings.auto_packaging: bool | Nonefield, with clear None-as-unset semantics.config.py:resolve_auto_packaging()reads the TTL cache and falls back to the startup-time value;apply_global_routing_configpropagates the setting at startup.app.py:_resolve_auto_packaging()wraps the config call with a fail-open exception handler; guards are inserted at the three relevant event paths;_render_canvas_without_packaging()handles the canvas-only render; unknown buttons now return{"status": "ignored"}instead of falling through.Confidence Score: 4/5
Safe to merge; the only finding is a cosmetic dead-parameter in a helper function. Core logic, cache behaviour, and fail-open default are all sound.
The toggle logic is straightforward and well-isolated behind the existing TTL-cache layer. The unknown-button fix is clearly correct. The single comment is about an unused
payloadargument in_resolve_auto_packagingthat doesn't affect runtime behaviour — removing it would improve clarity but nothing breaks with it present.docker/src/app.py — minor cleanup of the unused parameter in _resolve_auto_packaging and its three call-sites.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Incoming webhook event] --> B{Endpoint} B -->|canvas events| C[canvas handler] B -->|entry events| D[event handler] B -->|config.updated lifecycle| E[lifecycle handler] E --> E1[config.invalidate_routing_config] C --> F{userInteracted?} F -->|Yes| G{Known buttonId?} G -->|browse, page, back, metadata| H[Handle button action] G -->|update-package| I[handle_update_package - enqueue] G -->|unknown| J[Return ignored - NEW FIX] F -->|No - lifecycle event| K{_resolve_auto_packaging} K -->|False| L[render canvas without packaging Return 202] K -->|True| M[send updating canvas + publish_packaging_request Return 202] D --> N{event_type supported?} N -->|No| O[Return ignored] N -->|Yes| P{_resolve_auto_packaging} P -->|False| Q[Return skipped - NEW] P -->|True| R[publish_packaging_request - Return ACCEPTED] subgraph resolve[_resolve_auto_packaging closure] S[get_benchling_secrets] --> T[resolve_auto_packaging via TTL cache] T --> U{value in cache?} U -->|yes| V[return cached bool] U -->|no| W[return config.auto_packaging startup default] S -->|Exception| X[return True fail open] end%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A[Incoming webhook event] --> B{Endpoint} B -->|canvas events| C[canvas handler] B -->|entry events| D[event handler] B -->|config.updated lifecycle| E[lifecycle handler] E --> E1[config.invalidate_routing_config] C --> F{userInteracted?} F -->|Yes| G{Known buttonId?} G -->|browse, page, back, metadata| H[Handle button action] G -->|update-package| I[handle_update_package - enqueue] G -->|unknown| J[Return ignored - NEW FIX] F -->|No - lifecycle event| K{_resolve_auto_packaging} K -->|False| L[render canvas without packaging Return 202] K -->|True| M[send updating canvas + publish_packaging_request Return 202] D --> N{event_type supported?} N -->|No| O[Return ignored] N -->|Yes| P{_resolve_auto_packaging} P -->|False| Q[Return skipped - NEW] P -->|True| R[publish_packaging_request - Return ACCEPTED] subgraph resolve[_resolve_auto_packaging closure] S[get_benchling_secrets] --> T[resolve_auto_packaging via TTL cache] T --> U{value in cache?} U -->|yes| V[return cached bool] U -->|no| W[return config.auto_packaging startup default] S -->|Exception| X[return True fail open] endReviews (1): Last reviewed commit: "feat(app-manifest): subscribe to configu..." | Re-trigger Greptile