With the conditional workflow connectors (#561) and the automatic ascender_stats_* artifacts (#562) now merged, workflows can already branch on things like "did the previous job change anything" or "which hosts failed". While reviewing #562, @cigamit raised a good point: why stop at stats? We could expose data about the previous job itself, like whether it ran in check mode, what the limit was, the job id, and so on. That would let us model workflows much more programmatically. This thread is an attempt to turn that idea into a concrete list and gather feedback before writing any code.
What we have today
A job that runs a playbook automatically publishes these artifacts to its downstream nodes:
ascender_stats_changed, ascender_stats_failed (booleans), ascender_stats_changed_hosts, ascender_stats_non_changed_hosts, ascender_stats_failed_hosts, ascender_stats_non_failed_hosts (host name lists, truncated above ASCENDER_AUTO_STATS_MAX_HOSTS), and ascender_stats_hosts_truncated.
That covers "what happened", but a downstream node still knows nothing about "what actually ran". Here is what I think is worth adding, grouped from cheapest to most ambitious. None of it exposes secrets, everything below is already visible through the API for anyone with read access to the job, the point is making it reachable from inside the workflow without calling the API.
Group 1: identity and effective parameters of the job
All of this already lives on the Job model, so the cost is basically serialization:
job_id, job_template_id, job_template_name
job_type (run vs check) and diff_mode
- the effective
limit (after JT field, launch prompt and workflow node prompt are resolved)
job_tags and skip_tags
scm_revision the project checkout actually used
- inventory id and name
- execution environment, instance group and execution node
- launch type (manual, scheduled, webhook, relaunch, workflow) and who launched it
- verbosity, forks, and slice number/count for sliced jobs
Two worked examples of why the "effective parameters" matter:
Canary with a guardrail. Node A runs a playbook with limit: canary_hosts, a conditional connector checks it went well, node B does the full rollout. Today, if someone edits the template and drops the limit, your canary silently runs against everything, and then B runs against everything again. Nothing in the workflow can detect that. If the effective limit is exposed, the connector between A and B can require that A actually ran limited. The limit is also the compact, never truncated answer to "what subset did the previous node run on", which the host lists can't guarantee once they hit the truncation cap.
Check then apply. Node A runs the playbook in check mode, a connector routes to an approval node only if the dry run predicted changes, then node B applies for real. Exposing job_type lets the workflow verify that A really was a check run, and combined with ascender_stats_changed you get a proper plan/approve/apply pipeline out of existing building blocks.
Revision pinning for promotion pipelines. A staging node runs from main and its job records the exact commit it checked out. If someone pushes to main while the pipeline waits for approval, the production node will happily check out a newer commit than the one that was tested. Exposing scm_revision as an artifact is the first half of the fix. The second half is being able to feed it back in: projects with branch override enabled already accept a branch, tag or commit sha in scm_branch, but artifacts today only flow into the child job's extra vars, they cannot fill prompt fields like scm_branch. So this use case also argues for some form of artifact to prompt mapping, or the parameter redefinition approach discussed in #568. The two mechanisms complement each other: artifacts read from the past, parameter redefinition shapes what runs next.
As a precedent, Ascender already injects awx_project_revision and friends into the running job's own vars. The proposal is essentially to also surface that information towards the following nodes.
Group 2: stats we don't expose yet
Same playbook_on_stats event we already consume, so also cheap:
- unreachable hosts as their own list, separate from failed. They are different problems: an unreachable host may just be powered off, which enables workflows like "wake the machines, wait, retry only on
unreachable_hosts", while a failed host is a real error.
- counts for
skipped, rescued and ignored. A play with ignored > 0 "passed" but had errors swallowed by ignore_errors, and a quality gate may want to know.
hosts_matched, the total number of hosts the play ran against. This one covers Ansible's most dangerous silent failure: a limit or host pattern that matches nothing "succeeds" while doing nothing. A connector condition on hosts_matched > 0 turns that into a hard stop.
Group 3: timing
started, finished, elapsed, and time spent in queue. Enables guardrails like "if the backup took longer than X, notify" or "if jobs are sitting 20 minutes in queue, something is wrong with capacity", without leaving the workflow.
Group 4: node types that produce no artifacts at all today
- Project updates: a
revision_changed boolean plus old and new revision. That gives you "only run the deploy if the repo actually changed", which is the commit trigger of a CI system built from workflow pieces.
- Inventory syncs: hosts added and removed. "If the sync brought new hosts, run the bootstrap playbook on exactly those."
- Approval nodes: who approved and when, propagated so a final notification or audit node can report it.
Open design questions
- Flat keys vs nested. Flat
ascender_job_* keys are easiest to use in connector conditions. A single nested dict is cleaner for the full dump. Maybe both: flat for the fields you filter on, one dict for everything.
- Multiple parents. Artifacts merge as the workflow advances and the last writer wins. When a node converges from two parents, "the previous job" is ambiguous. One option is a per node namespace keyed by the workflow node
identifier, something like ascender_nodes.<identifier>.*, with the flat keys as sugar for the common linear case. This is where "programmatically model the workflows" really starts.
- Opt in and size. #562 set the precedent with
ASCENDER_AUTO_STATS_ENABLED and the host list cap. Job metadata is small and could arguably be always on, but it should probably ride the same switch.
- Naming.
ascender_job_* for metadata next to the existing ascender_stats_* for results? Other ideas welcome.
I'm happy to start sending PRs for this incrementally (group 1 and 2 first, most likely), but I'd rather agree here on the shape and the naming before writing code. Thoughts, and use cases I haven't considered, very welcome.