diff --git a/Cargo.lock b/Cargo.lock index e194d5c..14e3a4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3104,7 +3104,7 @@ dependencies = [ [[package]] name = "graphql-orm-ai" -version = "0.96.1" +version = "0.96.2" dependencies = [ "agql-auth", "async-graphql", diff --git a/crates/graphql-orm-ai/CHANGELOG.md b/crates/graphql-orm-ai/CHANGELOG.md index ebacf8a..b2cbe2a 100644 --- a/crates/graphql-orm-ai/CHANGELOG.md +++ b/crates/graphql-orm-ai/CHANGELOG.md @@ -18,6 +18,31 @@ checkpoint facts. For the current workspace baseline and active gates, use the [implementation status](docs/implementation-status.md) and the central [AI production-readiness plan](../../docs/plans/active/ai-production-readiness/README.md). +## [0.96.2] - 2026-09-02 + +Persistent schema module: **0.64.0** (unchanged from 0.96.1). + +### Fixed + +- The fixed capability broker now makes its empty argument, relationship and + root-bound wrapper fields genuinely optional. Its execute definition tells + providers to copy only paths admitted by the loaded `planSchema`, flatten + scalar argument leaves, and omit `maximumItems` unless that exact schema + exposes a root result bound. +- A scalar or custom object result no longer fails solely because a provider + was forced to invent an inapplicable root bound or send empty placeholder + collections around an otherwise valid closed query plan. + +### Security + +- The authoritative schema-derived compiler, exact loaded reference, + capability fingerprint, public selection allow-list, argument validation, + resolver authorization and result budgets remain unchanged. Optional broker + transport fields grant no additional query field or argument. + +There is no database, data, table, column, index, constraint, backfill, +protected-payload, GraphQL SDL, backup, or restore migration. + ## [0.96.1] - 2026-09-02 Persistent schema module: **0.64.0** (unchanged from 0.96.0). diff --git a/crates/graphql-orm-ai/Cargo.toml b/crates/graphql-orm-ai/Cargo.toml index 01ae181..90d0962 100644 --- a/crates/graphql-orm-ai/Cargo.toml +++ b/crates/graphql-orm-ai/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graphql-orm-ai" -version = "0.96.1" +version = "0.96.2" edition = "2024" authors = ["Toby Martin "] description = "Project-agnostic AI agent runtime for graphql-orm applications" diff --git a/crates/graphql-orm-ai/MIGRATION.md b/crates/graphql-orm-ai/MIGRATION.md index e293eb2..e8407ed 100644 --- a/crates/graphql-orm-ai/MIGRATION.md +++ b/crates/graphql-orm-ai/MIGRATION.md @@ -19,6 +19,23 @@ they describe. For the current workspace baseline and active delivery gates, use [implementation status](docs/implementation-status.md) and the central [AI production-readiness plan](../../docs/plans/active/ai-production-readiness/README.md). +## 0.96.1 to 0.96.2: schema-aligned fixed-broker execution wrappers + +Adopt `graphql-orm-ai` 0.96.2 from one reviewed full monorepo revision. No host +API or configuration change is required. Fixed-broker providers may now omit +empty `arguments`, `relationshipArguments`, `relationshipMaximumItems` and +`maximumItems` wrapper fields. `loadedReference` and at least one exact public +selection remain required. + +Providers should copy only fields admitted by the exact `planSchema` returned +by describe. Flatten supplied scalar argument leaves into the broker's +name/value entries, and supply `maximumItems` only when the loaded plan schema +exposes that property. The final schema-derived compiler and fresh resolver +authorization remain authoritative. + +The AI schema module remains **0.64.0**. There is no database, data, GraphQL +SDL, protected-payload, backup, restore, or data backfill migration. + ## 0.96.0 to 0.96.1: bounded assistant-item lifecycle continuation Adopt `graphql-orm-ai` 0.96.1 from one reviewed full monorepo revision. No host diff --git a/crates/graphql-orm-ai/README.md b/crates/graphql-orm-ai/README.md index a5d09f7..8a9c635 100644 --- a/crates/graphql-orm-ai/README.md +++ b/crates/graphql-orm-ai/README.md @@ -28,7 +28,7 @@ for AI, ORM, storage, backup, and tool-profile packages: ```toml [dependencies] -graphql-orm-ai = { git = "https://github.com/Dastari/graphql-orm.git", rev = "", version = "0.96.1", default-features = false, features = ["sqlite"] } +graphql-orm-ai = { git = "https://github.com/Dastari/graphql-orm.git", rev = "", version = "0.96.2", default-features = false, features = ["sqlite"] } ``` Exactly one persistence backend is required: `sqlite` (default), `postgres`, diff --git a/crates/graphql-orm-ai/docs/implementation-status.md b/crates/graphql-orm-ai/docs/implementation-status.md index 2df05c1..aaa35ff 100644 --- a/crates/graphql-orm-ai/docs/implementation-status.md +++ b/crates/graphql-orm-ai/docs/implementation-status.md @@ -10,7 +10,7 @@ supersedes: [] # Implementation Status -`graphql-orm-ai` is at crate version `0.96.1` with AI schema module +`graphql-orm-ai` is at crate version `0.96.2` with AI schema module `0.64.0`. It uses workspace `graphql-orm` `0.30.0`, backend-neutral `graphql-orm-ai-tool-profiles` `0.10.4`, and external `agql-auth` `0.19.0` at `1d2e9fe2e1576105212a7b340a11abf8cad0382d`. diff --git a/crates/graphql-orm-ai/src/capability_delivery.rs b/crates/graphql-orm-ai/src/capability_delivery.rs index 4e46aa2..43235fe 100644 --- a/crates/graphql-orm-ai/src/capability_delivery.rs +++ b/crates/graphql-orm-ai/src/capability_delivery.rs @@ -306,27 +306,42 @@ fn fixed_broker_definitions(index_fingerprint: &str) -> Vec let execute = broker_definition( AI_CAPABILITY_EXECUTE_TOOL_ID, "graphql_capabilities_execute", - "Execute one previously loaded exact capability using a closed public-name query plan.", + "Execute one previously loaded exact capability. Copy only fields admitted by the returned planSchema: flatten argument leaves into name/value entries, and omit optional wrapper fields that the planSchema does not expose.", index_fingerprint, json!({ "type": "object", "properties": { - "loadedReference": {"type": "string", "minLength": 64, "maxLength": 64}, + "loadedReference": { + "type": "string", "minLength": 64, "maxLength": 64, + "description": "Exact loadedReference returned by the matching describe call." + }, "arguments": { "type": "array", "maxItems": 64, + "description": "Optional root arguments admitted by planSchema.arguments. Flatten each supplied scalar leaf into one name/value entry using its dotted public path; omit this array when no root argument is needed.", "items": { "type": "object", "properties": { - "name": {"type": "string", "minLength": 1, "maxLength": 256}, - "value": {"type": ["string", "integer", "number", "boolean", "null"]} + "name": { + "type": "string", "minLength": 1, "maxLength": 256, + "description": "Exact dotted public argument path admitted by planSchema.arguments." + }, + "value": { + "type": ["string", "integer", "number", "boolean", "null"], + "description": "Scalar value for the exact argument path." + } }, "required": ["name", "value"], "additionalProperties": false } }, - "selections": {"type": "array", "maxItems": 256, "uniqueItems": true, "items": {"type": "string", "maxLength": 512}}, + "selections": { + "type": "array", "maxItems": 256, "uniqueItems": true, + "description": "One or more exact scalar paths copied from planSchema.selections.items.enum.", + "items": {"type": "string", "maxLength": 512} + }, "relationshipArguments": { "type": "array", "maxItems": 64, + "description": "Optional relationship arguments admitted by planSchema.relationshipArguments. Omit when that schema has no applicable relationship argument.", "items": { "type": "object", "properties": { @@ -350,6 +365,7 @@ fn fixed_broker_definitions(index_fingerprint: &str) -> Vec }, "relationshipMaximumItems": { "type": "array", "maxItems": 64, + "description": "Optional relationship bounds admitted by planSchema.relationshipMaximumItems. Omit when the exact relationship path is not present there.", "items": { "type": "object", "properties": { @@ -360,9 +376,12 @@ fn fixed_broker_definitions(index_fingerprint: &str) -> Vec "additionalProperties": false } }, - "maximumItems": {"type": ["integer", "null"], "minimum": 1, "maximum": 10000} + "maximumItems": { + "type": ["integer", "null"], "minimum": 1, "maximum": 10000, + "description": "Optional root result bound. Supply a positive value only when planSchema exposes maximumItems; otherwise omit it." + } }, - "required": ["loadedReference", "arguments", "selections", "relationshipArguments", "relationshipMaximumItems", "maximumItems"], + "required": ["loadedReference", "selections"], "additionalProperties": false }), ); @@ -3074,6 +3093,39 @@ mod tests { )); } + #[test] + fn fixed_broker_execute_admits_the_minimal_schema_aligned_wrapper() { + let definitions = fixed_broker_definitions(&"a".repeat(64)); + let execute = definitions + .iter() + .find(|definition| definition.tool_id == AI_CAPABILITY_EXECUTE_TOOL_ID) + .expect("fixed execute definition"); + let validator = jsonschema::validator_for(&execute.parameters) + .expect("fixed execute parameters should be valid JSON Schema"); + let minimal = json!({ + "loadedReference": "b".repeat(64), + "selections": ["records.id"] + }); + assert!(validator.is_valid(&minimal)); + let parsed: BrokerExecuteArguments = + serde_json::from_value(minimal).expect("minimal execute wrapper should decode"); + assert!(parsed.arguments.is_empty()); + assert!(parsed.relationship_arguments.is_empty()); + assert!(parsed.relationship_maximum_items.is_empty()); + assert_eq!(parsed.maximum_items, None); + + assert_eq!( + execute.parameters["required"], + json!(["loadedReference", "selections"]) + ); + assert!(execute.description.contains("returned planSchema")); + assert!( + execute.parameters["properties"]["maximumItems"]["description"] + .as_str() + .is_some_and(|description| description.contains("only when planSchema exposes")) + ); + } + #[tokio::test] async fn describe_exposes_compiler_owned_result_record_cost_bounds() { let principal = principal(); diff --git a/crates/graphql-orm-ai/src/providers/codex_app_server.rs b/crates/graphql-orm-ai/src/providers/codex_app_server.rs index 930ed26..82c3864 100644 --- a/crates/graphql-orm-ai/src/providers/codex_app_server.rs +++ b/crates/graphql-orm-ai/src/providers/codex_app_server.rs @@ -8109,6 +8109,18 @@ pub(crate) mod tests { execute.pointer("/inputSchema/properties/maximumItems/type"), Some(&json!(["integer", "null"])) ); + assert_eq!( + execute.pointer("/inputSchema/required"), + Some(&json!(["loadedReference", "selections"])) + ); + assert!( + jsonschema::validator_for(&execute["inputSchema"]) + .expect("projected execute schema") + .is_valid(&json!({ + "loadedReference": "b".repeat(64), + "selections": ["records.id"] + })) + ); assert!( execute .pointer("/inputSchema/properties/maximumItems/description") diff --git a/crates/graphql-orm/tests/fixtures/backend-coexistence/Cargo.lock b/crates/graphql-orm/tests/fixtures/backend-coexistence/Cargo.lock index 6048e64..26fdb09 100644 --- a/crates/graphql-orm/tests/fixtures/backend-coexistence/Cargo.lock +++ b/crates/graphql-orm/tests/fixtures/backend-coexistence/Cargo.lock @@ -1329,7 +1329,7 @@ dependencies = [ [[package]] name = "graphql-orm-ai" -version = "0.96.1" +version = "0.96.2" dependencies = [ "agql-auth", "async-graphql", diff --git a/docs/plans/active/ai-production-readiness/README.md b/docs/plans/active/ai-production-readiness/README.md index a255e1c..9e4c607 100644 --- a/docs/plans/active/ai-production-readiness/README.md +++ b/docs/plans/active/ai-production-readiness/README.md @@ -59,7 +59,7 @@ retention, or restore proofs remain closed. ## Current checkpoint -Package 0.96.1 and AI schema module 0.64.0 provide the protected runtime, +Package 0.96.2 and AI schema module 0.64.0 provide the protected runtime, provider adapters, exact completed-batch adoption, retention foundations, restore planning, and readiness observation contracts. Database-derived collection covers bounded conservative run classification, approval and diff --git a/docs/reference/workspace-packages.md b/docs/reference/workspace-packages.md index 668d40d..8553e8d 100644 --- a/docs/reference/workspace-packages.md +++ b/docs/reference/workspace-packages.md @@ -19,7 +19,7 @@ changes. | Package | Version | Path | Default features | Direct internal dependencies | | --- | --- | --- | --- | --- | | `graphql-orm` | `0.30.0` | `crates/graphql-orm` | `sqlite` | `graphql-orm-macros`, `graphql-orm-operation-catalog`, `graphql-orm-router-protocol` (dev-only) | -| `graphql-orm-ai` | `0.96.1` | `crates/graphql-orm-ai` | `sqlite` | `graphql-orm`, `graphql-orm-ai-tool-profiles`, `graphql-orm-storage` | +| `graphql-orm-ai` | `0.96.2` | `crates/graphql-orm-ai` | `sqlite` | `graphql-orm`, `graphql-orm-ai-tool-profiles`, `graphql-orm-storage` | | `graphql-orm-ai-tool-profiles` | `0.10.4` | `crates/graphql-orm-ai-tool-profiles` | none | `graphql-orm-operation-catalog`, `graphql-orm-router-protocol` (dev-only) | | `graphql-orm-backup` | `0.7.2` | `crates/graphql-orm-backup` | `local` | `graphql-orm` (optional), `graphql-orm-storage` | | `graphql-orm-macros` | `0.30.0` | `crates/graphql-orm-macros` | `sqlite` | none |