feat(handler): /workflows/v1/input-contract + default entrypoint resolution#1965
feat(handler): /workflows/v1/input-contract + default entrypoint resolution#1965OnkarVO7 wants to merge 7 commits into
Conversation
⛔ Snyk checks have failed. 1 issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
📜 Docstring Coverage ReportRESULT: PASSED (minimum: 30.0%, actual: 77.6%) Detailed Coverage Report |
📦 Trivy Vulnerability Scan Results
Report SummaryCould not generate summary table (data length mismatch: 9 vs 8). Scan Result Detailsrequirements.txtuv.lock |
📦 Trivy Secret Scan Results
Report SummaryCould not generate summary table (data length mismatch: 9 vs 8). Scan Result Detailsrequirements.txtuv.lock |
☂️ Python Coverage
Overall Coverage
New FilesNo new covered files... Modified FilesNo covered modified files...
|
|
🛠 Full Test Coverage Report: https://k.atlan.dev/coverage/application-sdk/pr/1965 |
|
/regen-manifest |
|
✅ Capability manifest regenerated and pushed. The updated SDK-surface diff is now visible in Files changed. |
Triggered by /regen-manifest on PR #1965. Generated by .claude/skills/capability-manifest.
🧪 Connector Tests — atlan-openapi-appStatus: Passed ✅
|
🧪 Connector Tests — atlan-mysql-appStatus: Passed ✅
|
|
@sdk-review |
Earlier @sdk-review trigger (click to expand)🔍 SDK Review (mothership) triggered by @cmgrote at 2026-06-03T14:26:57.749Z. 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 #1965 — feat(handler): /workflows/v1/input-contract + default entrypoint resolutionVerdict: NEEDS HUMAN REVIEW
PR Title Check
Findings
Holistic Recommendations
Strengths
CI: all passing |
| # ------------------------------------------------------------------ | ||
|
|
||
| @app.get("/workflows/v1/input-contract") | ||
| async def get_input_contract(entrypoint: str | None = None) -> Response: |
There was a problem hiding this comment.
[Important] [QUAL] — default-resolution + 404/400/500 mapping duplicated across start_workflow and get_input_contract
Evidence: start_workflow (around L972) and the new get_input_contract (this hunk, L1827–1896) both:
- Look up the app via
AppRegistry.get_instance().get(...). - If
?entrypoint=is present, fetch by name; otherwise callresolve_default_entrypoint(entry_points). - Return
404on missing app,400on missing/ambiguous EP,500on the registered-but-empty defensive case.
Two paths means two places to keep aligned; they're already mildly divergent (start_workflow includes the _ENTRYPOINT_NAME_RE check inside its body, this endpoint applies it before the registry lookup).
Path Forward: Extract a private helper, e.g.
def _resolve_app_entrypoint(
app_name: str, entrypoint: str | None
) -> tuple[AppMetadata, EntryPointMetadata]:
# raises HTTPException(404 / 400 / 500) on the same conditions
...and call it from both endpoints. The lazy-import pattern still works inside the helper. Reduces the surface area for future drift and shrinks both endpoint bodies.
PATCH-scope; safe follow-up if the design-change findings (L972 / base.py L654) are accepted as-is, since the helper would be the right place to encode whichever resolution policy is chosen.
Triggered by /regen-manifest on PR #1965. Generated by .claude/skills/capability-manifest.
1396646 to
7314648
Compare
|
@sdk-review |
|
🔍 SDK Review (mothership) triggered by @cmgrote at 2026-06-04T10:04:20.427Z. Watch the workflow run live — the review summary will appear as a separate comment when complete (typical: 5–30 min, hard cap 2h). |
Triggered by /regen-manifest on PR #1965. Generated by .claude/skills/capability-manifest.
7314648 to
474aed9
Compare
…resolution Expose each app's input contract as JSON Schema and let apps declare a default entrypoint so callers can omit ?entrypoint=. - GET /workflows/v1/input-contract returns the resolved entrypoint's AppInputContract.model_json_schema() (contract_cls resolved from AppRegistry), mirroring /workflows/v1/start entrypoint resolution. Heracles consumes this to validate request inputs and to detect credential fields by the CredentialRef/AgentCredentialSpec $ref. - @entrypoint(default=True) + EntryPointMetadata.default; resolve_default_entrypoint() picks the single entrypoint, or the one marked default on multi-entrypoint apps. Registration rejects >1 default. - /start and /input-contract both resolve the default when ?entrypoint= is omitted. Additive: only apps that opt into default=True change behavior; single-entrypoint and existing multi-entrypoint apps are unaffected. Part of BLDX-1354 (native app workflow APIs, app-contract-driven). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Triggered by /regen-manifest on PR #1965. Generated by .claude/skills/capability-manifest.
…as permanent default Restructures __init_subclass__ to collect both the implicit run() entry point and explicit @entrypoint-decorated methods into a unified registry, rather than treating the two paths as mutually exclusive. Rules: - run()-only: registered as a single implicit entry point (backward compat) - @entrypoint-only: registered as before; single ep is the default - mixed run() + @entrypoint(s): run() is always the default — an @entrypoint(default=True) alongside run() is an error - multiple @entrypoints with no explicit default: first alphabetically is auto-marked default; multiple explicit defaults is an error Tests cover all 8 registration scenarios and all handler invocability paths (every entry-point combination is reachable via POST /workflows/v1/start with and without ?entrypoint=).
Add a Default entrypoint resolution reference table to docs/concepts/entry-points.md, update docs/concepts/apps.md to note run()+@entrypoint coexistence, and add the same rules as a plain-text table to the application_sdk/app/entrypoint.py module docstring. Fixes the stale "multi-entry-point apps require ?entrypoint= (400 otherwise)" claim — multi-ep apps now auto-select the first alphabetical entry point when the param is omitted.
…t behavior test_multi_ep_missing_workflow_type_returns_400 expected 400 when ?entrypoint= was omitted on a multi-EP app. With auto-default now in place, the first-alphabetical entry point (route-a) is selected and the request returns 200. Updated to assert the successful dispatch.
…n and dispatch
- Rename resolve_default_entrypoint → _resolve_default_entrypoint (private)
- Sort _scan_entrypoints output by ep.name so auto-default is alphabetical,
matching docs ("first alphabetically" claim now accurate)
- Extract _collect_implicit_ep, _scan_entrypoints, _build_entry_points,
_apply_app_registration, _register_tasks out of base.py into a new private
module application_sdk/app/_ep_registration.py; base.py imports and
re-exports them — shrinks base.py by ~240 lines
- Slim App.__init_subclass__ from ~170 lines to ~45 by delegating to the
extracted helpers
- Add _resolve_app_entrypoint() module-level helper in service.py; both
/workflows/v1/start and /workflows/v1/input-contract now share one
resolution path — eliminates duplicated logic and fixes the broad
except Exception → 404 in get_input_contract (only AppNotFoundError maps
to 404; other failures bubble to 500)
- Include app name in "App not registered" 404 detail
- Add migration note in start_workflow for the multi-EP auto-default
behavioral change (previously returned 400, now dispatches alphabetical
first EP)
- Extract _register_workflow_routes() at module level in service.py;
create_app_handler_service now delegates all workflow/manifest/event/
config/file/dev routes to it — shrinks the factory by ~700 lines; related
helpers (_config_objectstore_key, _provision_local_vault, etc.) also moved
to module level
474aed9 to
c421fb7
Compare
…ime input_type (BLDX-1354) The /workflows/v1/input-contract endpoint returned ep.input_type — the type the @entrypoint run() method accepts. For contract-toolkit apps that is a thin runtime wrapper (e.g. bigquery's CrawlInput/MinerInput, extra="allow", just two fields) rather than the rich generated AppInputContract (all configured fields + CredentialRef-typed credential fields) at app/generated/{entrypoint}/_input.py. Result: validation no-ops, no field/default discovery, and Heracles can't detect credentials by $ref. Auto-discover the generated AppInputContract by convention (app/generated/{entrypoint}/_input.py, or app/generated/_input.py for single-ep apps) and serve it; fall back to the runtime input_type when no generated contract is importable, so apps not using the contract-toolkit are unchanged. Covered by new tests: generated contract preferred when present (and per-entry fallback for an entrypoint without one), plus a direct fallback unit test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Changelog
GET /workflows/v1/input-contract— returns the resolved entrypoint'sAppInputContract.model_json_schema()(the same Pydantic model/workflows/v1/startvalidates against). The orchestrator (Heracles) consumes it to validate request inputs and to detect credential fields by theCredentialRef/AgentCredentialSpec$ref.@entrypoint(default=True)+EntryPointMetadata.default, and aresolve_default_entrypoint()helper. Registration rejects more than one default./workflows/v1/startand/workflows/v1/input-contractboth resolve the default when?entrypoint=is omitted, so they behave consistently.run()and@entrypoint-decorated methods to coexist in the same App subclass.run()always holds permanent default-precedence in mixed apps; using@entrypoint(default=True)alongsiderun()is an error. Multiple@entrypoints with no explicit default auto-mark the first alphabetically as default.run()-only) apps are backward-compatible. Multi-entrypoint apps without any explicit default now get an auto-default (first alphabetically) rather than 400ing; apps that previously 400'd on/input-contractwithout?entrypoint=will now resolve.Entrypoint registration rules
run()onlyrun()is the implicit default (backward compat)@entrypoint@entrypoints, none explicit@entrypoints, onedefault=True@entrypoints, multipledefault=Truerun()+@entrypoint(s)run()is always the default;@entrypoint(default=True)raisesAdditional context (e.g. screenshots, logs, links)
/v1/app/*consumer lands separately.heracles/docs/v2-native-workflow-apis-plan.md.Checklist
Copyleft License Compliance
🤖 Generated with Claude Code