refactor(runtime): parse capability inputs before provider work - #1314
Conversation
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8a85ecbd90
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c562cede22
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| parsed_request = cast( | ||
| ContractModel, | ||
| parse_capability_input(self.spec.request_type, bounded_input), | ||
| ) |
There was a problem hiding this comment.
Preserve typed values when binding operation inputs
When an installed operation is invoked with request.inputs, _bind_inputs() has already resolved each URI to its typed domain ContractModel, but this call parses bounded_input, which was built using model_dump(mode="json"), instead of validating the assembled typed mapping. Every producer-to-consumer composition therefore round-trips the shared value through its wire representation and constructs a second value; a domain value with non-lossless serialization or backend-native state can be changed or rejected before execution. Strictly validate the agent-supplied fields first, then bind and validate the resolved Python values without serializing them.
AGENTS.md reference: AGENTS.md:L76-L83
Useful? React with 👍 / 👎.
* refactor(runtime): centralize capability result projection * refactor(runtime): parse capability inputs before provider work (#1314) * refactor(runtime): parse capability inputs once * docs(architecture): enforce typed adapter boundaries * fix(runtime): preserve request values before validation * fix(composition): preserve resolved typed value identity Carry a JSON accounting view beside each resolved input-port value, restore the exact validated object for strict Python-mode request binding, and keep ordinary caller payloads on the existing strict JSON parse. Add a regression proving both object identity and rejection of stringly external fields. * fix(composition): separate typed binding from JSON accounting Restore exact model instances at input ports, keep canonical JSON projection solely for request-size accounting, and parse the assembled typed payload directly in strict Python mode. External requests still cross the strict JSON parser, while direct and referenced composition preserve object identity. * fix(runtime): preserve typed dispatch values and outputs * fix exact replay preparation and relative import ratchet * fix exact replay request preparation import * fix exact replay prepared value handoff * fix exact verifier typing and relative result imports * test polytope adapter preparation handoff * test(lean): prepare typed requests in live smoke * fix(architecture): remove legacy checker mode marker
Problem
Jacobian's installed adapters had two request paths. Most adapters first ran a generic JSON Schema validator and then parsed the same payload into a Pydantic model, while marker-bearing adapters skipped that first pass. The split contradicted the parse-once architecture, allowed validation order to vary by adapter, and kept a schema-only extension path even though external operation packages are unsupported.
Removing the generic pass alone exposed two boundary gaps: Pydantic's default coercion could accept numeric strings that the catalog advertised as integers, and provider readiness could run before the adapter had validated its complete request. Published values also needed a typed invariant after removing the generic output-schema execution pass.
This PR is stacked on #1311, which centralizes final
CapabilityResultprojection.Relates to #1210, #1212, and #1219.
Solution
CapabilityAdapter[PreparedT]contract. Every installed adapter now strictly parses and prepares its typed request before provider readiness, then executes only that prepared value.strict=True, preserving enum wire values while rejecting coercions such as"21"toint. Non-JSON and over-limit inputs become boundedINVALID_REQUESTdiagnostics.model_constructvalues fail withADAPTER_RESULT_INVALID.LeanCheckRequestmodel, remove the descriptor-only registration facade, and update every surviving SAT/SMT, polynomial, graph, Lean, finite, and exact-checker adapter to the same boundary.No MCP tool shape, operation ID, artifact format, checker authorization rule, or mathematical implementation changes. The stricter parser intentionally makes runtime acceptance match the catalog's generated JSON Schema.
Testing
make lint typecheck— Ruff, formatting, complexity, and mypy passed; 459 source files typechecked.make test-unit— 877 passed on the final tree.make test-composition— 157 passed.make test-e2e— 4 passed.make test-provider— 64 passed.make import-contracts— 7 contracts kept.make architecture— 1,685 files checked.make test-architecture— 406 files checked.make docs-linkcheck— passed.Trust & Compatibility Impact
The adapter protocol is internal and pre-stable. Invalid requests now fail before provider readiness or execution, and invalid typed outputs fail before publication. Verification records, checker identity, evidence binding, and artifact lineage are unchanged.
External operation adapters remain unsupported; this change removes stale prose that implied an entry-point loader existed.
Architecture Budget
This deletes the
TypedInputAdaptercompatibility mode and the schema-first/schema-bypass branch. The shared strict parser and two-phase adapter contract replace both former production paths across all 58 installed adapters in this PR; no registry, facade, codec protocol, or external extension mechanism is added.Suggested review order:
capability_adapters.pyandcapability_dispatch.pyfor the parse/readiness/projection invariants.operation_installation.pyfor value-reference binding and typed execution.graphs/composition.py,polynomials/_support.py, andexact_domain_checkers.py.test_capability_adapter_authority.py, the graph numeric-string regression, and the architecture ratchet.Checklist