bolt icon indicating copy to clipboard operation
bolt copied to clipboard

Implement "outputs" specification for tasks

Open reidmv opened this issue 4 years ago • 3 comments

Use Case

Tasks exist as an alternative to scripts which provide the benefit of concrete, typed parameters for input, and optionally, structured output. All of this is in service to making them easy to auto-document, understand, and use as building blocks in plans.

While it's easy today to know what kind of data you need to input to tasks, it's still kind of a crapshoot what data that task will output, and to understand that you either have to human-read the README or maybe even RTFS.

Describe the Solution You Would Like

To make tasks easier to consume off-the-shelf in plans, we should add an outputs key to the task.json spec, a peer to the existing parameters key. This should define required or optional outputs to tasks, and their types.

If a task specifies a required output and does not provide it, that should be considered an error, and we should raise it as an appropriate type. Same thing if a task specifies a type for an output and that output is returned with a non-matching type. The error in this case should include the actual output in its details.

Output entries should support a description to be used for auto-documentation of tasks.

A special accommodation should be made for Sensitive type outputs. If an output is sensitive, the user should be able to indicate as such (e.g. "outputs": {"name": "password", "type": "String", "sensitive": true}) and the task should wrap the value in Sensitive() when returning it.

reidmv avatar Feb 25 '21 18:02 reidmv

Comments from Slack

Lucy Wyman Jul 7th at 5:14 PM For the tasks issue, why wouldn't the task author just check all of that in the task itself? Are metadata authors and task script authors really different often enough to justify specifying output keys? It also seems a bit heavy-handed for, like, trying to catch typos in the keys kind of thing.

Reid Vandewiele Jul 7th It’s more about consuming tasks you didn’t write yourself. For example: I’m working with a customer who has two teams, three modules.

The Linux team owns a low-level utility module which provides tasks that return data from a 3rd party service. This team maintains these low-level tasks both for their own use, and also for use by the Windows team.

The Windows team and the Linux team both own their own team-specific workflows modules. The workflows module contains plans which call/consume tasks from the low-level utility module.

Members of the Windows team did not write any tasks in the utility module, nor do they have any idea how they work. But to consume those tasks correctly, in their team’s workflows module, they need to know what data the low-level utility tasks produce.

Some members of the Linux team have no expertise with the low-level utility tasks, but do contribute to the Linux team’s own workflow plans. Similarly, they need to know how to use the utility tasks, without necessarily knowing anything about the code or implementation.

That’s the main value in defining clear outputs. Letting teams consume content they did not write. (edited)

reidmv avatar Jul 15 '21 13:07 reidmv

This issue has not had activity for 60 days and will be marked as stale. If this issue continues to have no activity for 7 days, it will be closed.

github-actions[bot] avatar Jul 29 '22 01:07 github-actions[bot]

It would be cool to be able to define error types as structured output. Something like

"outputs": { "..." }
"errors": {
    "someRecoverable": {
        "description": "I'm a recoverable error, and my Enum tells the caller what to try",
        "type": "Enum['bloop', 'blorp']" 
    }
    "someFatal": {
        "description": "Fatal error. Returns an [error code, message] tuple.",
        "type": "Tuple[Integar, String]",
        "default": "Tuple[-1, 'unknown error']"
    }
    "_": {
        "description": "Maybe we have some underscore handler with default behavior? Spitballing..."
    }
}

Perhaps, in Bolt plans, the "errors" block (if returned) gets marshaled into an object with a field that has an Enum['someRecoverable', 'someFatal'], then you can get the underlying error with a method.

The inspiration here is Rust's Result<T, E> types, where - very often - the E (error) variant is itself an enum.

dontlaugh avatar Aug 23 '22 22:08 dontlaugh

This issue has not had activity for 60 days and will be marked as stale. If this issue continues to have no activity for 7 days, it will be closed.

github-actions[bot] avatar Oct 23 '22 00:10 github-actions[bot]

This issue is stale and has been closed. If you believe this is in error, or would like the Bolt team to reconsider it, please reopen the issue.

github-actions[bot] avatar Oct 31 '22 00:10 github-actions[bot]