From 98f9ad980b2c64a89ce550c6eee45e69834d300f Mon Sep 17 00:00:00 2001 From: Damian Reeves <957246+DamianReeves@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:57:32 -0500 Subject: [PATCH] feat(workbench): define authenticated job HTTP --- .config/mise/config.toml | 4 + spec/workbench/README.md | 7 + spec/workbench/host/draft.1/README.md | 278 +++++++++++++ .../host/draft.1/schemas/error.schema.json | 36 ++ .../schemas/identity-request.schema.json | 29 ++ .../draft.1/schemas/release-reply.schema.json | 19 + .../schemas/session-descriptor.schema.json | 34 ++ .../schemas/submission-reply.schema.json | 57 +++ .../draft.1/schemas/submission.schema.json | 50 +++ spec/workbench/host/draft.1/tests/error.json | 102 +++++ .../host/draft.1/tests/identity-request.json | 124 ++++++ .../host/draft.1/tests/release-reply.json | 45 +++ .../draft.1/tests/session-descriptor.json | 279 +++++++++++++ .../host/draft.1/tests/submission-reply.json | 375 ++++++++++++++++++ .../host/draft.1/tests/submission.json | 265 +++++++++++++ spec/workbench/jobs/draft.1/README.md | 9 + 16 files changed, 1713 insertions(+) create mode 100644 spec/workbench/host/draft.1/README.md create mode 100644 spec/workbench/host/draft.1/schemas/error.schema.json create mode 100644 spec/workbench/host/draft.1/schemas/identity-request.schema.json create mode 100644 spec/workbench/host/draft.1/schemas/release-reply.schema.json create mode 100644 spec/workbench/host/draft.1/schemas/session-descriptor.schema.json create mode 100644 spec/workbench/host/draft.1/schemas/submission-reply.schema.json create mode 100644 spec/workbench/host/draft.1/schemas/submission.schema.json create mode 100644 spec/workbench/host/draft.1/tests/error.json create mode 100644 spec/workbench/host/draft.1/tests/identity-request.json create mode 100644 spec/workbench/host/draft.1/tests/release-reply.json create mode 100644 spec/workbench/host/draft.1/tests/session-descriptor.json create mode 100644 spec/workbench/host/draft.1/tests/submission-reply.json create mode 100644 spec/workbench/host/draft.1/tests/submission.json diff --git a/.config/mise/config.toml b/.config/mise/config.toml index 23438b213..89f6ab3e0 100644 --- a/.config/mise/config.toml +++ b/.config/mise/config.toml @@ -187,6 +187,10 @@ run = [ "jsonschema metaschema spec/workbench/draft.6/schemas", "jsonschema test spec/workbench/draft.6/tests -r spec/workbench/schemas -r spec/workbench/draft.5/schemas -r spec/workbench/jobs/draft.1/schemas -r spec/workbench/draft.6/schemas", "jsonschema validate spec/workbench/draft.6/schemas/mck-corpus.schema.json spec/workbench/mck/draft.6/cases.json -r spec/workbench/schemas -r spec/workbench/draft.5/schemas -r spec/workbench/jobs/draft.1/schemas -r spec/workbench/draft.6/schemas", + "jsonschema fmt spec/workbench/host/draft.1/schemas --check", + "jsonschema lint spec/workbench/host/draft.1/schemas -r spec/workbench/host/draft.1/schemas -r spec/workbench/jobs/draft.1/schemas -r spec/workbench/draft.5/schemas -r spec/workbench/schemas", + "jsonschema metaschema spec/workbench/host/draft.1/schemas", + "jsonschema test spec/workbench/host/draft.1/tests -r spec/workbench/host/draft.1/schemas -r spec/workbench/jobs/draft.1/schemas -r spec/workbench/draft.5/schemas -r spec/workbench/schemas", ] # Not part of `check`: website:build-schemas depends on website:install, whose diff --git a/spec/workbench/README.md b/spec/workbench/README.md index 7b59aaad7..53db7ec78 100644 --- a/spec/workbench/README.md +++ b/spec/workbench/README.md @@ -185,3 +185,10 @@ runner compares parent-owned fixed traces against an explicit implementation adapter and independently rechecks reports. The five historical kits, job receipt profile and nested terminal payload remain unchanged. This qualifies offline receipt behavior; live authenticated host/UI jobs remain separate. + +[Authenticated HTTP host](host/draft.1/README.md) defines the separate opt-in +`morphir/workbench/host` `0.1.0-draft.1` mapping for immutable submission, +query, cancellation and explicit terminal release. It authenticates each host +request through the existing connected v1 session and preserves its manifest +and RPC contract. Structural fixtures do not advertise live implementation +support or durable recovery. diff --git a/spec/workbench/host/draft.1/README.md b/spec/workbench/host/draft.1/README.md new file mode 100644 index 000000000..6c2f1d0a0 --- /dev/null +++ b/spec/workbench/host/draft.1/README.md @@ -0,0 +1,278 @@ +# Authenticated Workbench HTTP host + +This opt-in HTTP profile is named `morphir/workbench/host` and supports exactly +`0.1.0-draft.1`. It binds the existing +[job receipt profile](../../jobs/draft.1/README.md) to authenticated submission, +query, cancellation and explicit terminal release. It preserves the exact jobs +`0.1.0-draft.1` and terminal outcome `0.1.0-draft.5` contracts. + +The gateway uses the real Rust connected v1 host's `/launch` exchange and +HttpOnly session cookie. Connected `protocolVersion: 1`, its session manifest and +JSON-RPC methods remain unchanged. The separate host descriptor below is the +only live advertisement of this extension. Offline MCK capabilities, a visible +Evaluate button, a provider name or successful compilation do not advertise it. +An absent or disabled host extension leaves connected evaluation unavailable. + +## Authentication and origin + +The gateway serves the UI and this extension at one loopback HTTP origin. It +proxies the connected v1 `/launch` exchange without exposing the token or cookie +to application JavaScript. It preserves the upstream cookie's HttpOnly and +SameSite protections. JavaScript sends only same-origin requests with that +cookie. A gateway does not invent an authenticated session or accept a caller's +session label as proof. + +Every request under `/api/workbench/` requires a new successful authenticated +upstream `GET /api/session`, using that request's session cookie. This includes +query, cancellation and release. A cached successful descriptor is insufficient. +The gateway requires the exact expected connected v1 manifest shape and version; +it must not follow an upstream redirect to a different authority or forward the +cookie outside its configured loopback upstream. Authentication failure returns +401. An unreachable, timed-out or invalid upstream returns 503 and grants no +authority. A changed authenticated session cannot adopt a retained job and +returns 409 for that identity. + +All host endpoints require the configured loopback Host authority. A request +with an unexpected Host is rejected. Cross-site Fetch Metadata requests are +rejected. Every POST requires an explicit `Origin` exactly equal to the gateway's +HTTP origin, including scheme and port; missing, `null`, malformed, multiple or +different origins return 403. No CORS permission is granted. Mutating GET routes, +URL-carried job identities and launch credentials in host request bodies are +not supported. Host responses use `Cache-Control: no-store` and JSON content +with a declared UTF-8 encoding; they must not be reused as authenticated offers. + +The gateway owner creates a fresh opaque `hostId` at startup. It is not the +connected v1 `sessionId` or an execution `providerId`. The separately selected +provider is an explicit bounded Scheme execution provider for this first slice. +Provider selection does not grant metadata authority or reuse another provider's +identity. A process restart creates a different host authority, even if an +upstream session cookie survives. + +## Negotiation + +`GET /api/workbench/session` returns HTTP 200 and exactly: + +```json +{ + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": ["query", "cancel"] + } +} +``` + +The offer's `sessionId` must equal the freshly authenticated v1 session ID. +`query` is required; `cancel` is optional. The operation list describes receipt +operations, so `submit` and `release` are not added to it. The host descriptor +implies the submission contract in this document. Cancellation remains disabled +unless the offer includes it. A disabled extension or unavailable cancellation +operation returns 404 rather than an invented successful receipt. + +Unknown profile names, versions, build suffixes and members reject. This exact +prerelease does not accept a released `0.1.0` or a later draft. Implementations +that select versions through ranges must use an established SemVer library. +All five identity fields retain the jobs profile's 1 to 128 UTF-16 unit limits. + +## One immutable submission + +`POST /api/workbench/submit` accepts exactly: + +```json +{ + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"},{\"type\":\"int\",\"value\":\"3\"}]" + } +} +``` + +The model field is Morphir IR JSON source text, not a filesystem path, URL, +workspace handle or executable source. The example illustrates the wire shape; +a runnable model also needs the selected public entry. The implementation must +admit the declared IR revision and may migrate a private copy to its execution +revision. It must reject unsupported or malformed IR before execution. It must +not read a path or fetch a URL found in either source field. + +`entry` is a public fully qualified function name containing 1 to 1,024 UTF-16 +units. The host admits the model, public entry, declared argument types, +constructor registry and bounded provider support before accepting an execution +claim. A caller cannot select a host, session, provider, snapshot, job or call ID +through this body. The accepted immutable snapshot contains this exact admitted +model, entry, arguments and provider binding. Subsequent UI edits cannot alter it. + +`inputs` has exactly `format` and `source`: + +| Format | Source document | +| --- | --- | +| `json` | One JSON array of zero to 64 tagged argument values from the [value codec](../../README.md#json-values). | +| `ion-text` | One unannotated Ion list of zero to 64 argument values using the [published Ion mapping](../../README.md#invocation-suites-and-ion). | + +For example, JSON arguments `[{"type":"int","value":"2"}]` and Ion arguments +`[2]` denote the same one-argument list. Empty arguments are `[]` in both formats. +Ion Bool, Int and Decimal use their native scalar types; Unit is +`morphir_unit::null`; the remaining typed values use one `morphir_value` +annotation on their tagged struct. An Ion datagram containing several values, +Ion binary source, an invocation suite envelope or an untyped JSON number array +is not an argument-list document for this endpoint. Exactly one source value +must consume the entire input. Codec limits and declared admission still apply, +including depth 64, 100,000 aggregate value nodes/code units per call, numeric +bounds and unique record fields. The model and argument source are part of the +same HTTP body budget, not separate allowances. This endpoint has no extensions +metadata field. + +Successful admission returns HTTP 200 and exactly: + +```json +{ + "identity": { + "hostId": "host", "sessionId": "session", "providerId": "scheme", + "jobId": "job", "snapshotId": "snapshot" + }, + "calls": ["workbench"], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", "sessionId": "session", "providerId": "scheme", + "jobId": "job", "snapshotId": "snapshot" + }, + "sequence": 0, "state": "submitted", "cancelRequested": false, + "outcomes": null + } +} +``` + +The caller captures the top-level identity and fixed inventory `['workbench']` +as expected authority from this authenticated submission. The identity's first +three fields must equal the previously authenticated offer. The receipt's full +identity must equal the top-level identity. The client rejects a mismatch before +creating its receipt cursor. The initial receipt is always sequence 0, +`submitted`, with false acknowledgement and null outcomes, even if execution +completes before the HTTP reply arrives. A later query observes later revisions. + +The owner admits at most one execution claim per accepted job. Query, cancel and +reconnect do not execute a second time. A failed submission response can leave +submission acceptance unknown; this profile supplies no idempotency key or +submission discovery. Clients must not automatically retry it or relabel a new +job as recovery of that attempt. + +## Query, cancellation and release + +`POST /api/workbench/query`, `/api/workbench/cancel` and +`/api/workbench/release` share this exact body: + +```json +{ + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", "sessionId": "session", "providerId": "scheme", + "jobId": "job", "snapshotId": "snapshot" + } +} +``` + +The authenticated session and every identity field must match retained owner +state. An unknown or explicitly released job returns 404. Another host, provider +or snapshot cannot borrow a matching job ID. A changed session rejects rather +than opening an old snapshot under new authority. + +Query and cancellation return HTTP 200 and a raw existing jobs receipt, without +a wrapper or another version field. Every receipt follows the existing binding, +sequence, monotonic acknowledgement, immutable terminal and ordered outcome +rules. Query does not increase sequence just because it was polled. Terminal +outcomes contain exactly the one call ID `workbench` and must be admitted against +the submitted public manifest. `Result.Err` is returned data. SDK model errors, +primary failures, cancellation and separate cleanup evidence remain distinct. + +Cancellation acknowledges the request immediately with the owner receipt; that +receipt does not claim execution has terminated. Repeated acknowledgement does +not invent a new revision. The host then terminates its isolated execution worker +and completes cleanup before publishing cancelled or failed terminal evidence. +An execution result already committed by the owner may win the race and remain +returned data after acknowledgement. Cancelling a terminal job returns its +retained immutable terminal. Missing negotiated cancellation returns 404. + +Release is explicit terminal retention cleanup. Successful release returns HTTP +200 and exactly `{"released":true}`. Release before terminal returns 409 and +cannot stop work or discard evidence. Hosts may impose a bounded retained-job +capacity, rejecting new submissions with 409 when full. They must not silently +evict another job or overwrite retained evidence to make room. Clients need not +release a job to inspect its final receipt, but should release terminal jobs they +no longer need. This draft does not promise retention after owner restart. + +## Disconnect and bounded failures + +HTTP failure, timeout, browser reload and lost transport leave the last admitted +receipt intact. They never create cancelled or failed terminal evidence. To +reconcile an existing job, obtain a newly authenticated descriptor and compare +the exact previous host, session, provider, draft and operation set. Reconnect +then permits query of the existing immutable identity. A successful same-session +query uses the existing stale, replay and advance rules. Reconnect never submits +again. An offer mismatch leaves the old receipt disconnected and requires a +separate explicitly supported recovery contract. + +Every raw host request and response frame is at most 1,048,576 bytes of UTF-8, +measured before JSON parsing or response publication. The gateway must stop +reading oversized bodies and must bound upstream authentication responses too. +Reject invalid UTF-8, multiple JSON values, duplicate object members at every +depth, unknown members and unknown contract versions. Decode nested model JSON +and JSON argument source with duplicate-member rejection as well. Raw HTTP body +limits remain necessary even when a JSON Schema string length check passes. +Parsers and decoders must impose their depth and aggregate budgets before +unbounded recursive allocation. A response that cannot fit the frame budget +must not publish a truncated receipt or a successful incomplete outcome. + +Non-2xx errors have exactly this shape: + +```json +{"error":{"code":"workbench.invalid_request","message":"Invalid request"}} +``` + +The code contains 1 to 128 UTF-16 units; message contains 1 to 512. Errors must +not echo model or argument source, launch tokens, cookies, authentication +headers, provider stderr or stack traces. Hosts return bounded explanatory text; +clients must not treat an error message as executable content or terminal job +evidence. The following statuses define the transport categories: + +| HTTP status | Category | +| --- | --- | +| 400 | Invalid shape, unsupported version, source decoding or admission failure. | +| 401 | Connected v1 cookie authentication failed. | +| 403 | Origin, Host or cross-site request rejected. | +| 404 | Disabled extension/operation or unknown/released job. | +| 409 | Invalid transition, changed session or exhausted retained-job capacity. | +| 413 | Request/frame or admission budget exceeded. | +| 503 | Required upstream authentication or execution service unavailable. | + +An HTTP error after submission is transport evidence only. The owner may still +hold a running or terminal job. The client must query after exact same-session +reconciliation rather than synthesizing a terminal outcome. + +## Structural fixtures and implementation evidence + +The six closed schemas reference the existing jobs offer, identity and receipt +schemas, which in turn reference frozen terminal and value schemas. Run +`mise run workbench:schema-check` for formatting, lint, metaschema and the +independently authored positive/negative fixtures. Historical Workbench schema +and MCK paths remain unchanged. No additional compatibility runner is introduced. + +Schemas establish parsed structural shape. They do not establish authentication, +raw-byte budgets, duplicate JSON members, UTF-16 identity limits, session and +cross-field identity equality, valid IR, source decoding, provider support, +execution claims, cancellation races or worker cleanup. Fixtures deliberately +include structurally valid opaque source and mismatched identities to expose +those runtime obligations. Host acceptance tests must exercise the real v1 +launch/session flow and isolated execution rather than fake-cookie behavior. + +This first profile supports an opt-in HTTP gateway and in-memory same-session +reconciliation. Durable recovery, native-provider IPC, generated browser +execution, metadata trust grants and job transfer between sessions remain +separate work. Publication of this draft alone does not claim those capabilities. diff --git a/spec/workbench/host/draft.1/schemas/error.schema.json b/spec/workbench/host/draft.1/schemas/error.schema.json new file mode 100644 index 000000000..edf135f8e --- /dev/null +++ b/spec/workbench/host/draft.1/schemas/error.schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/error.schema.json", + "title": "Workbench host bounded HTTP error", + "description": "Closed non-success error without source text or credentials. Schema validation does not establish absence of sensitive text", + "examples": [ + { + "error": { + "code": "workbench.invalid_request", + "message": "Invalid request" + } + } + ], + "type": "object", + "required": [ "error" ], + "properties": { + "error": { + "type": "object", + "required": [ "code", "message" ], + "properties": { + "code": { + "type": "string", + "maxLength": 128, + "minLength": 1 + }, + "message": { + "type": "string", + "maxLength": 512, + "minLength": 1 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/spec/workbench/host/draft.1/schemas/identity-request.schema.json b/spec/workbench/host/draft.1/schemas/identity-request.schema.json new file mode 100644 index 000000000..2dd50ec12 --- /dev/null +++ b/spec/workbench/host/draft.1/schemas/identity-request.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/identity-request.schema.json", + "title": "Workbench host identity request", + "description": "Exact body shared by query, cancellation and terminal release. Authentication and identity ownership are semantic requirements", + "examples": [ + { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + } + } + ], + "type": "object", + "required": [ "contractVersion", "identity" ], + "properties": { + "contractVersion": { + "const": "0.1.0-draft.1" + }, + "identity": { + "$ref": "https://morphir.finos.org/spec/workbench/jobs/0.1.0-draft.1/identity.schema.json" + } + }, + "additionalProperties": false +} diff --git a/spec/workbench/host/draft.1/schemas/release-reply.schema.json b/spec/workbench/host/draft.1/schemas/release-reply.schema.json new file mode 100644 index 000000000..6c3b1356e --- /dev/null +++ b/spec/workbench/host/draft.1/schemas/release-reply.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/release-reply.schema.json", + "title": "Workbench host terminal release reply", + "description": "Successful explicit release of retained terminal evidence. The host must reject release before terminal", + "examples": [ + { + "released": true + } + ], + "type": "object", + "required": [ "released" ], + "properties": { + "released": { + "const": true + } + }, + "additionalProperties": false +} diff --git a/spec/workbench/host/draft.1/schemas/session-descriptor.schema.json b/spec/workbench/host/draft.1/schemas/session-descriptor.schema.json new file mode 100644 index 000000000..4cd95932d --- /dev/null +++ b/spec/workbench/host/draft.1/schemas/session-descriptor.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/session-descriptor.schema.json", + "title": "Authenticated Workbench host descriptor", + "description": "Exact opt-in host HTTP profile containing the authenticated jobs offer. Authentication and session equality are semantic requirements", + "examples": [ + { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ "query", "cancel" ] + } + } + ], + "type": "object", + "required": [ "profile", "contractVersion", "offer" ], + "properties": { + "profile": { + "const": "morphir/workbench/host" + }, + "contractVersion": { + "const": "0.1.0-draft.1" + }, + "offer": { + "$ref": "https://morphir.finos.org/spec/workbench/jobs/0.1.0-draft.1/offer.schema.json" + } + }, + "additionalProperties": false +} diff --git a/spec/workbench/host/draft.1/schemas/submission-reply.schema.json b/spec/workbench/host/draft.1/schemas/submission-reply.schema.json new file mode 100644 index 000000000..ccee33010 --- /dev/null +++ b/spec/workbench/host/draft.1/schemas/submission-reply.schema.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/submission-reply.schema.json", + "title": "Workbench host initial submission reply", + "description": "Expected identity and fixed call inventory with the exact initial submitted receipt. Identity equality across fields requires runtime admission", + "examples": [ + { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ "workbench" ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + } + ], + "type": "object", + "required": [ "identity", "calls", "receipt" ], + "properties": { + "identity": { + "$ref": "https://morphir.finos.org/spec/workbench/jobs/0.1.0-draft.1/identity.schema.json" + }, + "calls": { + "const": [ "workbench" ] + }, + "receipt": { + "$ref": "https://morphir.finos.org/spec/workbench/jobs/0.1.0-draft.1/receipt.schema.json", + "properties": { + "sequence": { + "const": 0 + }, + "state": { + "const": "submitted" + }, + "cancelRequested": { + "const": false + } + } + } + }, + "additionalProperties": false +} diff --git a/spec/workbench/host/draft.1/schemas/submission.schema.json b/spec/workbench/host/draft.1/schemas/submission.schema.json new file mode 100644 index 000000000..d9cd1e0f4 --- /dev/null +++ b/spec/workbench/host/draft.1/schemas/submission.schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/submission.schema.json", + "title": "Workbench host submission request", + "description": "One immutable in-memory model source and one public entry invocation. Source decoding and whole UTF-8 frame budgets require runtime admission", + "examples": [ + { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + } + ], + "type": "object", + "required": [ "contractVersion", "model", "entry", "inputs" ], + "properties": { + "contractVersion": { + "const": "0.1.0-draft.1" + }, + "model": { + "type": "string", + "maxLength": 1048576, + "minLength": 1 + }, + "entry": { + "type": "string", + "maxLength": 1024, + "minLength": 1 + }, + "inputs": { + "type": "object", + "required": [ "format", "source" ], + "properties": { + "format": { + "enum": [ "json", "ion-text" ] + }, + "source": { + "type": "string", + "maxLength": 1048576, + "minLength": 0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/spec/workbench/host/draft.1/tests/error.json b/spec/workbench/host/draft.1/tests/error.json new file mode 100644 index 000000000..0f9c7beb5 --- /dev/null +++ b/spec/workbench/host/draft.1/tests/error.json @@ -0,0 +1,102 @@ +{ + "target": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/error.schema.json", + "tests": [ + { + "description": "Bounded error", + "data": { + "error": { + "code": "workbench.invalid_request", + "message": "Invalid request" + } + }, + "valid": true + }, + { + "description": "Bounded maximum error text", + "data": { + "error": { + "code": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "message": "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" + } + }, + "valid": true + }, + { + "description": "Error code required content", + "data": { + "error": { + "code": "", + "message": "Invalid request" + } + }, + "valid": false + }, + { + "description": "Error code upper bound", + "data": { + "error": { + "code": "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "message": "Invalid request" + } + }, + "valid": false + }, + { + "description": "Message required content", + "data": { + "error": { + "code": "workbench.invalid_request", + "message": "" + } + }, + "valid": false + }, + { + "description": "Message upper bound", + "data": { + "error": { + "code": "workbench.invalid_request", + "message": "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" + } + }, + "valid": false + }, + { + "description": "Message must be text", + "data": { + "error": { + "code": "workbench.invalid_request", + "message": {} + } + }, + "valid": false + }, + { + "description": "No source echo", + "data": { + "error": { + "code": "workbench.invalid_request", + "message": "Invalid request", + "source": "secret model" + } + }, + "valid": false + }, + { + "description": "No credentials", + "data": { + "error": { + "code": "workbench.invalid_request", + "message": "Invalid request" + }, + "cookie": "secret" + }, + "valid": false + }, + { + "description": "Error object required", + "data": {}, + "valid": false + } + ] +} diff --git a/spec/workbench/host/draft.1/tests/identity-request.json b/spec/workbench/host/draft.1/tests/identity-request.json new file mode 100644 index 000000000..3a4b67e16 --- /dev/null +++ b/spec/workbench/host/draft.1/tests/identity-request.json @@ -0,0 +1,124 @@ +{ + "target": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/identity-request.schema.json", + "tests": [ + { + "description": "Exact request shared by query cancel release", + "data": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + } + }, + "valid": true + }, + { + "description": "Unknown draft", + "data": { + "contractVersion": "0.1.0-draft.2", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + } + }, + "valid": false + }, + { + "description": "Empty job ID", + "data": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "", + "snapshotId": "snapshot" + } + }, + "valid": false + }, + { + "description": "Snapshot length bound", + "data": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" + } + }, + "valid": false + }, + { + "description": "Unknown identity member", + "data": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot", + "path": "/tmp/model" + } + }, + "valid": false + }, + { + "description": "No client receipt override", + "data": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "Identity required", + "data": { + "contractVersion": "0.1.0-draft.1" + }, + "valid": false + }, + { + "description": "Version required", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + } + }, + "valid": false + } + ] +} diff --git a/spec/workbench/host/draft.1/tests/release-reply.json b/spec/workbench/host/draft.1/tests/release-reply.json new file mode 100644 index 000000000..52d67a585 --- /dev/null +++ b/spec/workbench/host/draft.1/tests/release-reply.json @@ -0,0 +1,45 @@ +{ + "target": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/release-reply.schema.json", + "tests": [ + { + "description": "Successful release", + "data": { + "released": true + }, + "valid": true + }, + { + "description": "No negative success reply", + "data": { + "released": false + }, + "valid": false + }, + { + "description": "Released must be boolean", + "data": { + "released": "true" + }, + "valid": false + }, + { + "description": "No missing release status", + "data": {}, + "valid": false + }, + { + "description": "Unknown release member", + "data": { + "released": true, + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + } + }, + "valid": false + } + ] +} diff --git a/spec/workbench/host/draft.1/tests/session-descriptor.json b/spec/workbench/host/draft.1/tests/session-descriptor.json new file mode 100644 index 000000000..0d5279db7 --- /dev/null +++ b/spec/workbench/host/draft.1/tests/session-descriptor.json @@ -0,0 +1,279 @@ +{ + "target": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/session-descriptor.schema.json", + "tests": [ + { + "description": "Exact authenticated descriptor", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ] + } + }, + "valid": true + }, + { + "description": "Query-only descriptor", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query" + ] + } + }, + "valid": true + }, + { + "description": "Operation ordering is not authority", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "cancel", + "query" + ] + } + }, + "valid": true + }, + { + "description": "Wrong host profile", + "data": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ] + } + }, + "valid": false + }, + { + "description": "Future host draft", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.2", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ] + } + }, + "valid": false + }, + { + "description": "Build suffix rejected", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1+build", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ] + } + }, + "valid": false + }, + { + "description": "Offer version independent and exact", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ] + } + }, + "valid": false + }, + { + "description": "Query required", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "cancel" + ] + } + }, + "valid": false + }, + { + "description": "Duplicate operations", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "query" + ] + } + }, + "valid": false + }, + { + "description": "Unknown operation", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "submit" + ] + } + }, + "valid": false + }, + { + "description": "Empty host identity", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ] + } + }, + "valid": false + }, + { + "description": "Identity code point upper bound", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp", + "operations": [ + "query", + "cancel" + ] + } + }, + "valid": false + }, + { + "description": "Unknown descriptor member", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ] + }, + "rpcVersion": 2 + }, + "valid": false + }, + { + "description": "Unknown nested offer member", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1", + "offer": { + "profile": "morphir/workbench/jobs", + "contractVersion": "0.1.0-draft.1", + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "operations": [ + "query", + "cancel" + ], + "recovery": true + } + }, + "valid": false + }, + { + "description": "Offer required", + "data": { + "profile": "morphir/workbench/host", + "contractVersion": "0.1.0-draft.1" + }, + "valid": false + } + ] +} diff --git a/spec/workbench/host/draft.1/tests/submission-reply.json b/spec/workbench/host/draft.1/tests/submission-reply.json new file mode 100644 index 000000000..1449a3bd9 --- /dev/null +++ b/spec/workbench/host/draft.1/tests/submission-reply.json @@ -0,0 +1,375 @@ +{ + "target": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/submission-reply.schema.json", + "tests": [ + { + "description": "Initial bound submission", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": true + }, + { + "description": "Cross-field identity equality is semantic", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "different", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": true + }, + { + "description": "One fixed invocation ID", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "other" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "No multi-call submission", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench", + "second" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "Inventory cannot be empty", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "Initial sequence exactly zero", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 1, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "Initial state submitted", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "running", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "No initial cancellation acknowledgement", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": true, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "Receipt exact version", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.2", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "Unknown reply member", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + }, + "token": "secret" + }, + "valid": false + }, + { + "description": "Unknown nested receipt member", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null, + "status": "ok" + } + }, + "valid": false + }, + { + "description": "Expected identity required", + "data": { + "calls": [ + "workbench" + ], + "receipt": { + "contractVersion": "0.1.0-draft.1", + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "sequence": 0, + "state": "submitted", + "cancelRequested": false, + "outcomes": null + } + }, + "valid": false + }, + { + "description": "Receipt required", + "data": { + "identity": { + "hostId": "host", + "sessionId": "session", + "providerId": "scheme", + "jobId": "job", + "snapshotId": "snapshot" + }, + "calls": [ + "workbench" + ] + }, + "valid": false + } + ] +} diff --git a/spec/workbench/host/draft.1/tests/submission.json b/spec/workbench/host/draft.1/tests/submission.json new file mode 100644 index 000000000..e32dbe24c --- /dev/null +++ b/spec/workbench/host/draft.1/tests/submission.json @@ -0,0 +1,265 @@ +{ + "target": "https://morphir.finos.org/spec/workbench/host/0.1.0-draft.1/submission.schema.json", + "tests": [ + { + "description": "JSON tagged argument source", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": true + }, + { + "description": "Ion native argument list", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "ion-text", + "source": "[2,true,morphir_unit::null]" + } + }, + "valid": true + }, + { + "description": "No arguments", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[]" + } + }, + "valid": true + }, + { + "description": "Model source is structurally opaque", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "invalid IR", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": true + }, + { + "description": "Input source decoding is semantic", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "invalid JSON" + } + }, + "valid": true + }, + { + "description": "Empty entry", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Entry code point upper bound", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Model must be source text", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": {}, + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Model cannot be absent text", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Unknown argument format", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "ion-binary", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Arguments source must be text", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": [] + } + }, + "valid": false + }, + { + "description": "Unknown input member", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]", + "path": "/tmp/args" + } + }, + "valid": false + }, + { + "description": "No path substitution", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + }, + "modelPath": "/tmp/model.json" + }, + "valid": false + }, + { + "description": "Caller cannot choose provider", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + }, + "providerId": "other" + }, + "valid": false + }, + { + "description": "Caller cannot choose call inventory", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + }, + "calls": [ + "other" + ] + }, + "valid": false + }, + { + "description": "Future submission version", + "data": { + "contractVersion": "0.1.0-draft.2", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Released version does not match draft", + "data": { + "contractVersion": "0.1.0", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Inputs required", + "data": { + "contractVersion": "0.1.0-draft.1", + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add" + }, + "valid": false + }, + { + "description": "Model required", + "data": { + "contractVersion": "0.1.0-draft.1", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + }, + { + "description": "Version required", + "data": { + "model": "{\"formatVersion\":3,\"distribution\":[\"Library\",[],[],{\"modules\":[]}]}", + "entry": "sample:main#add", + "inputs": { + "format": "json", + "source": "[{\"type\":\"int\",\"value\":\"2\"}]" + } + }, + "valid": false + } + ] +} diff --git a/spec/workbench/jobs/draft.1/README.md b/spec/workbench/jobs/draft.1/README.md index 58e2a7afd..4ac321d3d 100644 --- a/spec/workbench/jobs/draft.1/README.md +++ b/spec/workbench/jobs/draft.1/README.md @@ -134,3 +134,12 @@ then qualify host adapters and bind query/cancel transport methods. It selects this exact job profile and the frozen draft.5 terminal envelope. Its offline capability claim does not enable live query or cancellation controls; those require a separately qualified host adapter and authenticated transport. + +## Authenticated HTTP mapping + +[Host draft.1](../../host/draft.1/README.md) maps this unchanged receipt profile +to an opt-in same-origin loopback HTTP gateway. Each request authenticates +through the existing connected v1 session. The mapping adds immutable model +submission and explicit terminal release, with one fixed invocation inventory, +while retaining query/cancellation receipts and same-session reconnect rules. +Its separate descriptor is required before enabling live connected controls.