Skip to content

feat: add structured output support via action result schema#129

Open
jbguerraz wants to merge 2 commits intomainfrom
feat/structured-output
Open

feat: add structured output support via action result schema#129
jbguerraz wants to merge 2 commits intomainfrom
feat/structured-output

Conversation

@jbguerraz
Copy link

@jbguerraz jbguerraz commented Feb 5, 2026

Summary

Implements #128 - Structured Output via Action Metadata Schema

This PR adds support for declarative result schemas in action metadata, enabling structured JSON/YAML output and automatic text formatting.

Changes

  • Add DefResult type and Result field to DefAction for parsing result schemas from YAML
  • Add RuntimeResultProvider interface and WithResult composition for runtimes that return structured data
  • Add NewFnRuntimeWithResult for function-based runtimes returning results
  • Extend RunInfo to capture Result from RuntimeResultProvider
  • Add --json and --yaml global flags via jsonoutput plugin for structured output
  • Add outputJSON/outputYAML/outputText handlers in actionscobra plugin
  • Generic text formatting: unwrap result wrappers and output key-value pairs
  • Shell runtime: capture stdout as JSON when action has result schema
  • Container runtime: capture stdout as JSON when action has result schema
  • Add NewCapturingStreams helper for stdout capture
  • Error output includes code field (e.g., EXIT_1) when available

Usage

Actions define a result schema in their YAML:

result:
  type: object
  properties:
    packages:
      type: array
      items:
        type: object
        properties:
          name:
            type: string
          ref:
            type: string

Go-based actions

Use NewFnRuntimeWithResult to return structured data:

action.SetRuntime(action.NewFnRuntimeWithResult(func(ctx context.Context, a *action.Action) (any, error) {
    // ... execute action
    return result, nil
}))

Shell-based actions

Simply echo valid JSON to stdout:

#!/bin/bash
echo '{"packages": [{"name": "foo", "ref": "v1.0.0"}]}'

Container-based actions

Same as shell - echo valid JSON to stdout from within the container:

CMD ["sh", "-c", "echo '{\"status\": \"ok\"}'"]

Output Formats

Output is handled automatically by launchr:

  • Default: key-value text format
  • --json: structured JSON wrapped in {"result": ...} or {"error": ...}
  • --yaml: structured YAML with result: or error: keys

Error Handling

On error with --json:

{
  "error": {
    "message": "action failed with exit code 1",
    "code": "EXIT_1"
  }
}

Exit code is preserved for proper shell scripting.

Example

# Text output (default)
$ myctl model:list
name: plasma-core
ref: prepare
name: plasma-work
ref: 1.16.2

# JSON output
$ myctl model:list --json
{
  "result": {
    "packages": [
      {"name": "plasma-core", "ref": "prepare"},
      {"name": "plasma-work", "ref": "1.16.2"}
    ]
  }
}

# YAML output
$ myctl model:list --yaml
result:
  packages:
    - name: plasma-core
      ref: prepare
    - name: plasma-work
      ref: 1.16.2

Backward Compatibility

Actions without result schemas continue to work as before.

Coverage of Issue #128

Requirement Status
Parse result field from action.yaml
--json global flag
--yaml global flag
RuntimeResultProvider interface
Shell runtime support
Container runtime support
Error handling with code
Stream separation (stdout/stderr)
Backward compatibility

Closes #128

@jbguerraz jbguerraz force-pushed the feat/structured-output branch 4 times, most recently from 3064e66 to 978a00b Compare February 5, 2026 02:57
@jbguerraz jbguerraz force-pushed the feat/structured-output branch 2 times, most recently from 11a4ea3 to 3fbec5d Compare February 5, 2026 10:15
@jbguerraz
Copy link
Author

and some love for the ci as benefits :)

@jbguerraz jbguerraz force-pushed the feat/structured-output branch from a0b0d46 to 5ad6d76 Compare February 6, 2026 23:28
- Disable fail-fast in test matrix so one failing OS doesn't cancel others
- Retry Docker ContainerCreate on transient gRPC errors (Windows Desktop)
- Pin Windows runner to windows-2022 (windows-latest broke WSL interop)
- Mark Windows tests as continue-on-error until WSL fixed upstream
@jbguerraz jbguerraz force-pushed the feat/structured-output branch from f830504 to ed7c6dc Compare March 5, 2026 11:25
Add structured output support (JSON/YAML) via action result schemas.
Actions define a result schema in their YAML and return data via Result().
When --output json or --output yaml is used (-o for short), terminal
output is silenced and only the encoded result goes to stdout.

- Actions define result schema in action.yaml and implement Result() any
- NewFnRuntimeWithResult for actions returning structured data
- Terminal silenced in structured mode (text output via term in normal mode)
- Single --output/-o flag replaces previous --json/--yaml boolean flags
- Builder --output renamed to --out-file to avoid global flag collision
- Error on unknown format values

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@jbguerraz jbguerraz force-pushed the feat/structured-output branch from ed7c6dc to 56faf20 Compare March 5, 2026 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal: Structured Output via Action Metadata Schema

1 participant