Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/open-collection-gap-analysis/AGENT_GOAL_PROMPTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ If an agent host does not inject the project-local skills, tell the agent to rea
/goal Complete OC-080 runtime lifecycle support for WebSocket and unary gRPC using $missio-agent-coordination, $missio-runtime-implementer, and $missio-demo-server-fixtures without stopping until before/after scripts, assertions, tests, actions, runtime variable mutation, response UI/tool output, local demo WebSocket and gRPC runtime fixtures, AGENT_PROGRESS updates, and complete automated unit, integration, security, failure-path, and cleanup tests are implemented and passing.
```

## Runtime Authoring UX

```text
/goal Complete OC-110 runtime authoring UX using $missio-agent-coordination and $missio-editor-schema-implementer without stopping until the visual request editor can create, edit, disable, reorder, and remove runtime scripts, tests, assertions, and set-variable actions; preserves schema-valid YAML for HTTP, GraphQL, WebSocket, and gRPC requests; includes user-verifiable demo guidance; updates AGENT_PROGRESS; and complete automated editor, model, validation, round-trip, and regression tests are implemented and passing.
```

## gRPC Streaming

```text
Expand All @@ -73,5 +79,5 @@ If an agent host does not inject the project-local skills, tell the agent to rea
## Final Integration

```text
/goal Complete final OpenCollection compatibility integration across all Missio tracks using $missio-agent-coordination without stopping until all OC-000 through OC-100 task rows are Done, all GitButler branches or PRs are linked, the full test suite and required fixture integration tests pass, documentation is consistent, and AGENT_PROGRESS.md contains final verification evidence.
/goal Complete final OpenCollection compatibility integration across all Missio tracks using $missio-agent-coordination without stopping until all OC-000 through OC-110 task rows are Done, all GitButler branches or PRs are linked, the full test suite and required fixture integration tests pass, documentation is consistent, and AGENT_PROGRESS.md contains final verification evidence.
```
54 changes: 52 additions & 2 deletions docs/open-collection-gap-analysis/AGENT_PROGRESS.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/open-collection-gap-analysis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ If a scenario truly cannot be automated in this repo, document the reason, manua
| OC-080 | Protocol Runtime Lifecycle | [08-runtime-non-http-protocols.md](tasks/08-runtime-non-http-protocols.md) | Apply scripts, assertions, tests, actions, and runtime variables to supported non-HTTP executors. |
| OC-090 | gRPC Streaming | [09-grpc-streaming.md](tasks/09-grpc-streaming.md) | Client, server, and bidirectional gRPC streaming execution, UI, fixtures, and tests. |
| OC-100 | Request Type UX | [10-request-type-ux.md](tasks/10-request-type-ux.md) | UI request type selection, read-only type visibility, and Bruno/Postman-aligned workflow review. |
| OC-110 | Runtime Authoring UX | [11-runtime-authoring-ux.md](tasks/11-runtime-authoring-ux.md) | Visual editor authoring for scripts, tests, assertions, and set-variable actions. |

## Project Skills

Expand All @@ -92,6 +93,7 @@ OC-000 through OC-040 and OC-060 have removed most of the original HTTP-only fou
| Importers and exporters need explicit protocol preservation or unsupported-conversion diagnostics. | [src/importers](../../src/importers) |
| Snippet export remains HTTP-oriented and should not silently accept non-HTTP requests. | [src/services/snippetService.ts](../../src/services/snippetService.ts) |
| Runtime scripts/assertions/actions are verified for HTTP and GraphQL-over-HTTP; WebSocket and gRPC need lifecycle parity. | [src/services/requestExecutionService.ts](../../src/services/requestExecutionService.ts) |
| Runtime scripts/assertions/actions can run, but common authoring still requires YAML edits. | [src/webview/requestPanel.ts](../../src/webview/requestPanel.ts) |
| gRPC unary execution exists, but streaming request types remain an explicit unsupported path. | [src/services/grpcClient.ts](../../src/services/grpcClient.ts) |

## Definition Of Done For Any Track
Expand Down
99 changes: 99 additions & 0 deletions docs/open-collection-gap-analysis/tasks/11-runtime-authoring-ux.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# OC-110 Runtime Authoring UX

## Goal

Make OpenCollection runtime behavior editable from Missio's visual request editor. Users should be able to create and maintain scripts, tests, assertions, and set-variable actions without hand-editing YAML for common workflows.

## Current Gap

OC-040 added the runtime engine and result display. OC-080 extends runtime execution to WebSocket and gRPC. The remaining gap is authoring: runtime features exist in YAML and response output, but the request editor does not provide a first-class UI for managing them.

| Surface | Gap |
| --- | --- |
| Runtime scripts | Users must hand-edit `runtime.scripts[]` for `before-request`, `after-response`, and `tests` script code. |
| Assertions | Users must hand-edit `runtime.assertions[]` with expression, operator, expected value, disabled state, and descriptions. |
| Actions | Users must hand-edit `runtime.actions[]`, including `set-variable` phase, selector, target scope, and variable name. |
| Ordering and disabled state | Users cannot reorder runtime entries or toggle them from the visual editor. |
| YAML safety | A UI implementation must preserve unsupported-but-valid runtime fields and existing script/action data. |
| Documentation | User-facing docs and demo notes do not yet explain visual runtime authoring workflows. |

## Implementation Plan

1. Audit the current runtime schema and editor serializer:

| Area | Work |
| --- | --- |
| Schema | Review `runtime.scripts`, `runtime.assertions`, `runtime.actions`, request defaults, and top-level script files. |
| Editor model | Confirm no-op request editor saves preserve all runtime fields, including unknown valid fields. |
| Response UI | Confirm runtime result display remains output-only and is not conflated with authoring controls. |
| Existing demos | Use `examples/demo-api/Runtime/` as real authoring examples. |

2. Design the request editor runtime section:

| Need | Expected Behavior |
| --- | --- |
| Runtime entry point | Add a clear request-editor section or tab for runtime behavior. |
| Scripts | Add, remove, disable, reorder, and edit script entries with phase selection and code text. |
| Tests | Support test script authoring as `runtime.scripts[]` entries with `type: tests`. |
| Assertions | Add, remove, disable, reorder, and edit assertion expression/operator/expected value/description. |
| Actions | Add, remove, disable, reorder, and edit `set-variable` actions with phase, selector method/expression, target scope, and variable name. |
| Inheritance | Show collection/folder/request defaults clearly when available, without silently flattening inherited defaults into the request. |

3. Preserve schema-native YAML:

| Requirement | Behavior |
| --- | --- |
| No data loss | Unknown but schema-valid runtime fields survive visual no-op edits. |
| Stable ordering | Reordering in the UI produces deterministic YAML arrays. |
| Disabled entries | Disabled scripts/assertions/actions remain present and do not get dropped. |
| Protocol neutrality | The authoring UI works for HTTP, GraphQL, WebSocket, and gRPC request files without adding protocol-specific roots. |

4. Add documentation and user-verifiable examples:

| Area | Work |
| --- | --- |
| Demo notes | Update runtime demo request descriptions if needed so users know what to edit and what result to expect. |
| Wiki/docs | Document visual runtime authoring and the YAML escape hatch for advanced runtime fields. |
| Smoke guidance | Include local demo server instructions for verifying scripts, assertions, tests, and actions. |

## Dependencies

| Task | Impact |
| --- | --- |
| OC-040 | Provides runtime engine, result model, sandbox behavior, assertions, tests, and actions. |
| OC-060 | Provides no-op round-trip safety patterns for schema-valid runtime data. |
| OC-070 | May touch request editor import/export surfaces; coordinate before editing shared request panel files. |
| OC-080 | Defines non-HTTP runtime request/response shapes; authoring should remain protocol-neutral and not assume HTTP-only response data. |
| OC-100 | Provides request type identity in the editor shell; runtime authoring should fit into that layout without competing with protocol controls. |

## Acceptance Criteria

| Requirement | Acceptance |
| --- | --- |
| Script authoring | Visual editor can create, edit, disable, remove, and reorder before-request, after-response, and test scripts. |
| Assertion authoring | Visual editor can create, edit, disable, remove, and reorder runtime assertions with supported operators and expected values. |
| Action authoring | Visual editor can create, edit, disable, remove, and reorder `set-variable` actions with phase, selector, and target controls. |
| Round-trip safety | Existing runtime YAML, including disabled entries and unknown schema-valid fields, survives no-op and edited saves. |
| Protocol neutrality | Runtime authoring works for HTTP, GraphQL, WebSocket, and gRPC request files. |
| Usability | Users can verify common runtime workflows from the visual editor using local demo requests. |
| Tests | Complete automated editor/model/round-trip/validation tests cover happy paths, disabled entries, ordering, preservation, and protocol regressions. |

## Suggested Tests

| Test | Expected Result |
| --- | --- |
| Add runtime script in editor model. | Saved YAML contains a schema-valid `runtime.scripts[]` entry with the selected phase and code. |
| Add assertion and action in editor model. | Saved YAML contains schema-valid `runtime.assertions[]` and `runtime.actions[]` entries. |
| Disable and reorder runtime entries. | Disabled state and array order persist after save/reload. |
| No-op edit of existing runtime fixture. | Existing scripts, assertions, actions, and unknown schema-valid fields are preserved. |
| Protocol fixture coverage. | HTTP, GraphQL, WebSocket, and gRPC requests can save runtime edits without stale protocol roots or validation failures. |
| UI shell regression. | Runtime authoring controls render with stable labels, controls, empty states, and no overlap with response Runtime results. |

## Out Of Scope

| Area | Reason |
| --- | --- |
| Runtime execution semantics | Covered by OC-040 and OC-080. |
| Protocol-specific snippet export | Covered by OC-070. |
| Postman script/event import conversion | Covered by OC-070 import/export work. |
| Interactive script debugger | This task covers authoring and save/round-trip behavior, not breakpoints or step debugging. |
16 changes: 16 additions & 0 deletions examples/demo-api/WebSocket/runtime-assertion-failure.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
info:
name: WebSocket runtime assertion failure
type: websocket
seq: 7
websocket:
url: "{{wsBaseUrl}}/ws/reject"
runtime:
scripts:
- type: tests
code: |-
test("websocket error response is available", () => assert(response.json().error.message.includes("401")));
assertions:
- expression: res.status
operator: equals
value: "101"
docs: Run `node examples/demo-api/server.js`, select the LOCAL environment, then send this request to see a terminal WebSocket upgrade error preserved with runtime test/assertion output.
51 changes: 51 additions & 0 deletions examples/demo-api/WebSocket/runtime-lifecycle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
info:
name: WebSocket runtime lifecycle
type: websocket
seq: 6
websocket:
url: "{{wsBaseUrl}}/ws/auth"
message:
type: json
data: '{"user":"before-runtime","count":0}'
runtime:
auth:
type: bearer
token: "{{demoToken}}"
variables:
- name: runtimeSocketCount
value: "7"
scripts:
- type: before-request
code: |-
missio.variables.set("runtimeSocketUser", "Ada Runtime");
missio.request.headers.set("X-Demo-Client", "missio-demo");
missio.request.headers.set("X-Runtime-Header", "scripted-websocket");
missio.request.body = {
user: missio.variables.get("runtimeSocketUser"),
count: Number(missio.variables.get("runtimeSocketCount"))
};
- type: after-response
code: |-
const inbound = response.json().events.find(event => event.direction === "inbound");
const payload = JSON.parse(inbound.data);
const message = payload.message.value || payload.message;
console.log("runtime websocket user", message.user);
- type: tests
code: |-
const inbound = response.json().events.find(event => event.direction === "inbound");
const payload = JSON.parse(inbound.data);
const message = payload.message.value || payload.message;
test("runtime websocket message echoed", () => assert(message.user === "Ada Runtime"));
assertions:
- expression: res.body.messageCount
operator: equals
value: "1"
actions:
- type: set-variable
selector:
method: jsonq
expression: $.messageCount
variable:
scope: runtime
name: runtimeSocketMessages
docs: Run `node examples/demo-api/server.js`, select the LOCAL environment, then send this request to see WebSocket before/after scripts, assertions, tests, and set-variable actions.
62 changes: 62 additions & 0 deletions examples/demo-api/gRPC/runtime-unary-lifecycle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
info:
name: gRPC unary runtime lifecycle
type: grpc
seq: 8
grpc:
url: "{{grpcBaseUrl}}"
method: missio.demo.DemoService/EchoUnary
methodType: unary
protoFilePath: proto/services/missio_demo.proto
metadata:
- name: x-demo-default
value: runtime-default-{{grpcRequestId}}
- name: x-demo-request
value: before-runtime
message: |-
{
"name": "before-runtime",
"userId": 0,
"trace": {
"requestId": "before-runtime"
}
}
runtime:
auth:
type: bearer
token: "{{grpcToken}}"
variables:
- name: grpcRuntimeUserId
value: "77"
scripts:
- type: before-request
code: |-
missio.variables.set("grpcRuntimeName", "Runtime Ada");
missio.variables.set("grpcRuntimeTrace", "runtime-" + missio.variables.get("grpcRequestId"));
missio.request.metadata.set("x-demo-request", "script-" + missio.variables.get("grpcRequestId"));
missio.request.body = {
name: missio.variables.get("grpcRuntimeName"),
userId: Number(missio.variables.get("grpcRuntimeUserId")),
trace: { requestId: missio.variables.get("grpcRuntimeTrace") }
};
- type: after-response
code: |-
console.info("runtime grpc request", response.json().requestId);
- type: tests
code: |-
test("runtime grpc message echoed", () => assert(response.json().name === "Runtime Ada"));
assertions:
- expression: res.body.name
operator: equals
value: Runtime Ada
- expression: res.body.userId
operator: equals
value: "77"
actions:
- type: set-variable
selector:
method: jsonq
expression: $.requestId
variable:
scope: runtime
name: grpcRuntimeRequestId
docs: Run `node examples/demo-api/grpc-server.js`, select the LOCAL environment, then send this request to see unary gRPC metadata/message mutation plus runtime tests, assertions, logs, and set-variable actions.
1 change: 1 addition & 0 deletions examples/demo-api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ function attachWebSocketFixtures(httpServer) {
route,
authorized: true,
client: req.headers['x-demo-client'] || null,
runtimeHeader: req.headers['x-runtime-header'] || null,
message: parseSocketMessage(data, isBinary),
}, null, 2));
return;
Expand Down
37 changes: 37 additions & 0 deletions schema/missio-extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
{
"extension": "gRPC streaming message sequences",
"reason": "The upstream schema defines GrpcRequestMessage but does not wire ordered request-message arrays into GrpcRequestDetails.message. Missio supports client-streaming and bidirectional-streaming by accepting ordered GrpcRequestMessage arrays while preserving the upstream unary string and message-variant shapes."
},
{
"extension": "non-HTTP runtime actions and WebSocket assertions",
"reason": "Missio runs the same scripts, assertions, tests, and set-variable actions across supported protocol executors. The upstream schema omits some runtime lifecycle fields from WebSocket and gRPC runtime objects, so Missio extends them for protocol parity."
}
],
"definitions": {
Expand Down Expand Up @@ -185,6 +189,39 @@
]
}
}
},
{
"description": "Allow runtime actions on gRPC requests",
"target": "$defs/GrpcRequestRuntime/properties",
"properties": {
"actions": {
"type": "array",
"description": "Array of runtime actions",
"items": {
"$ref": "#/$defs/Action"
}
}
}
},
{
"description": "Allow assertions and runtime actions on WebSocket requests",
"target": "$defs/WebSocketRequestRuntime/properties",
"properties": {
"assertions": {
"type": "array",
"description": "Array of assertions for response validation",
"items": {
"$ref": "#/$defs/Assertion"
}
},
"actions": {
"type": "array",
"description": "Array of runtime actions",
"items": {
"$ref": "#/$defs/Action"
}
}
}
}
]
}
21 changes: 21 additions & 0 deletions schema/opencollectionschema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2280,6 +2280,13 @@
},
"auth": {
"$ref": "#/$defs/Auth"
},
"actions": {
"type": "array",
"description": "Array of runtime actions",
"items": {
"$ref": "#/$defs/Action"
}
}
},
"additionalProperties": false
Expand Down Expand Up @@ -2380,6 +2387,20 @@
},
"auth": {
"$ref": "#/$defs/Auth"
},
"assertions": {
"type": "array",
"description": "Array of assertions for response validation",
"items": {
"$ref": "#/$defs/Assertion"
}
},
"actions": {
"type": "array",
"description": "Array of runtime actions",
"items": {
"$ref": "#/$defs/Action"
}
}
},
"additionalProperties": false
Expand Down
3 changes: 3 additions & 0 deletions src/models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ export interface GrpcRequestRuntime {
variables?: Variable[];
scripts?: Scripts;
assertions?: Assertion[];
actions?: Action[];
auth?: Auth;
}

Expand Down Expand Up @@ -537,6 +538,8 @@ export interface WebSocketRequestDetails {
export interface WebSocketRequestRuntime {
variables?: Variable[];
scripts?: Scripts;
assertions?: Assertion[];
actions?: Action[];
auth?: Auth;
}

Expand Down
2 changes: 1 addition & 1 deletion src/panels/requestPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { HttpRequest, OpenCollectionRequest, RequestDefaults, MissioCollect
import { getItemKind, isGraphQLRequest, isGrpcRequest, isHttpRequest, isProtocolRequest, isWebSocketRequest } from '../models/types';
import { requestLog, type ResolvedRequest } from '../services/httpClient';
import type { RequestExecutionService } from '../services/requestExecutionService';
import { exportRequest, findTarget, EXPORT_TARGETS } from '../services/snippetExporter';
import { exportRequest, findTarget, EXPORT_TARGETS, getUnsupportedSnippetDiagnostic } from '../services/snippetExporter';
import { resolveFileVariantToBuffer } from '../services/fileBodyHelper';
import type { CollectionService } from '../services/collectionService';
import type { EnvironmentService } from '../services/environmentService';
Expand Down
Loading
Loading