Task: Add the run configuration and address phase results by role
Table of Contents
This page documents a task in the Run a full report: scheduling, reporting and compute story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
Give the report definition a run configuration, carry it into the workflow start message, and make the workflow build the phase list that configuration names. At the same time replace the positional results[N] access with a lookup by phase role, because a second chain shifts every index after the phase it adds.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Run a full report: scheduling, reporting and compute |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-09-27 |
3. Acceptance
- A definition's run configuration selects the phases that run, and a run is reproducible from the database alone.
- No step consumer reads an earlier result by index; a missing result fails loudly instead of yielding an empty field.
- A definition whose configuration executes every phase still runs the existing chain unchanged.
4. Plan
Four parts, in the order each depends on the one before it.
- Results addressed by role. A step result becomes a named pair rather than a
bare JSON string:
workflow_step_resultcarries the step's name and its response,workflow_step_resultsis the list of them, andfind_step_resultanswers the payload the named step produced. The engine builds that list from every forwarded step, an empty answer included, because the list it built before skipped empty answers and so its positions were not step indices. A builder that cannot find or cannot read what it consumes throws; the engine catches that at dispatch and starts compensation, so a missing result fails the run instead of publishing a command with an empty field. - The run configuration. The definition table gains
pre_processing,prepared_input_keyandpost_processing, so the choice of phase is definition state and a run is reproducible from the database alone. The trigger validates the codes, refuses a substituting definition that names no archive, and copies the configuration intoreport_execution_request, the payload the engine stores and hands tobuild_steps. - The chains. One registered definition builds either chain from that
payload.
executeandexecuteare the seven steps the definition ran before;substitutereplaces the four pre-processing steps with one that resolves the named archive;ignorereplaces result collection with one that records the batch and leaves it unread. Submission takes the name of whichever step filled the input role, so no consumer is told which producer ran. - The substitutes. Both are reporting operations, and both answer in the result type of the phase they replace. That shared answer is what makes them indistinguishable to the step after them, and it is why the substitute requests name an existing result struct rather than a type of their own.
Deliberately not done here, and owned elsewhere in the story: the instance does not record which phases were substituted (the substituted-pipeline task adds it), retrieval is not yet separated from result collection, and resolving the prepared archive does not read it, so nothing checks that it is a valid engine input or digests it.
5. Measurements
Every number this task claims about a tree, with the command that produced it
and the commit it was measured at. A number without both is not a measurement,
because a clean-up record describes a tree and trunk moves under a branch: a
consumer count, a gate's component count and a suite's size all change with no
code changing, and the reader of a bare number cannot tell which tree it came
from. Where the raw output matters, put it in an evidence/ file beside the
task and name that file in the Command column.
| What | Value | Command | Commit | ||
|---|---|---|---|---|---|
| Step consumers that read a previous result by index | 0 (was 8) | =grep -rnE "(results\ | prev)\[[0-9]+\]" projects/ores.reporting/api/include/ores.reporting.api/workflow projects/ores.refdata/api/include/ores.refdata.api/workflow \ | wc -l= | f4ffb3c216 |
build_command definitions carried to the named-result signature |
5 | grep -rln build_command projects/ --include=*.hpp --include=*.cpp |
f4ffb3c216 |
||
Steps the execute and execute configuration builds |
7 | ores.reporting.api.tests "[workflow][reporting]" |
f4ffb3c216 |
||
Steps the substitute and ignore configuration builds |
4 | ores.reporting.api.tests "[workflow][reporting]" |
f4ffb3c216 |
||
| Chain cases in the new suite | 10 cases, 31 assertions | ores.reporting.api.tests "[workflow][reporting]" |
f4ffb3c216 |
||
ores.reporting.api.tests |
64 cases, 248 assertions | ores.reporting.api.tests |
f4ffb3c216 |
||
ores.shell.reporting.tests |
108 cases, 283 assertions (was 102 and 269) | ores.shell.reporting.tests |
f4ffb3c216 |
||
ores.workflow.core.tests |
17 cases, 85 assertions | ores.workflow.core.tests |
f4ffb3c216 |
||
| Schema validator warnings | 0, on the rebased tree (was 4 before main disabled the workspace SQL facet) |
projects/ores.sql/utility/validate_schemas.sh |
f4ffb3c216 |
||
| Reporting codegen drift | none | projects/ores.codegen/scripts/check_component_drift.py --component reporting |
f4ffb3c216 |
6. Notes
6.1. Decisions
- The run configuration is definition state: one entry per substitutable phase, with the prepared archive's location beside it rather than in the environment. A substituted run is therefore recognisable from the definition row before it runs, and an operator asking why a run produced no results reads the answer from the database.
- The chain is a pure function of
report_execution_request, because the engine rebuilds it on every dispatch and on recovery. The configuration therefore travels in the start message and not only in the definition. - A consumer addresses a producer by role, and the role resolves to a step name when the chain is built: submission is handed the name of whichever step filled the input role. Neither the phase nor its substitute knows about the other, and no step knows its own position.
- The two substitutes are implemented here rather than left to the proof task. A configuration that can select a phase must select one that can run; leaving the subjects unhandled would have made a substituting definition hang. What the proof task adds is the live run: a real grid, a real archive, and the instance's record of what was substituted.
- The DQ publish function gives a definition it publishes the
executeandexecuteconfiguration. A DQ artefact describes what to report, not how much of the pipeline to run, so the artefact schema is unchanged and publishing behaves as it did.
6.2. A codegen CLI inconsistency, found while regenerating
./compass.sh codegen generate --model projects/ores.reporting/modeling/ores.reporting.operations.org
--address ores.cpp.shell-command reports that it has nothing to generate, and
it says the same for ores.iam.account_messages.org, a model this change does
not touch. The component path does generate them: the drift gate's in-place
regeneration of reporting wrote the shell commands for both new messages, and
the shell suite grew from 102 to 108 cases. The two paths disagree about whether
the drawer's :ores.cpp.shell-command.enabled: true override applies to an
operation model, so the single-model form cannot be used to preview this facet.
The generated output is right; only the preview is not, and that is not fixed
here.
6.3. Applying the schema
The table change reaches the environment by recreation, which is the only
migration this project has. Two seed paths write definitions and both were given
the configuration explicitly: the FSM fixture in populate/reporting so the
fixture states what it runs, and the DQ publish function so its choice is read
where the definition is written. Neither was left to a column default, because
the table has no defaults and a definition that states nothing is not
reproducible.
7. Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario)
that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
8. PRs
| PR | Title |
|---|---|
| #2289 | [reporting] Select a report's phases from its run configuration |
9. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
10. Result
A report definition now says how much of the pipeline a run executes, and the workflow builds the chain that configuration names.
The run configuration is three columns on ores_reporting_report_definitions_tbl
— pre_processing, prepared_input_key and post_processing — so it is
definition state and a run is reproducible from the database with no operator
present. The trigger validates the codes, refuses a substituting definition that
names no archive, and copies the configuration into the workflow start payload,
which the engine rebuilds the chain from on every dispatch and on recovery.
Step results are addressed by the name of the step that produced them. The engine
now carries an entry for every forwarded step rather than only the ones that
answered, because the old list's positions were not step indices and one silent
step shifted every later result. A builder that cannot find or read what it
consumes throws, and the engine turns that into compensation, so a missing result
fails the run instead of publishing a command with an empty field. Five
build_command definitions moved to the signature; the provisioning workflow,
the only other consumer that read a result by position, reads save_account by
name.
One registered definition builds both chains. execute and execute are the
seven steps the definition ran before; substitute replaces the four
pre-processing steps with resolve_prepared_input; ignore replaces result
collection with ignore_compute_results. Both substitutes are reporting
operations and both answer in the result type of the phase they replace, which is
what makes them indistinguishable to the step after them, and submission is given
the name of whichever step filled the input role, so neither phase knows about the
other.
Deferred on purpose, and owned by other tasks in the story: the instance does not record which phases were substituted, retrieval is not yet separated from result collection, and resolving a prepared archive does not read it, so nothing checks that it is a valid engine input or digests it.
The branch also carries one unrelated fix. The clean-http change stopped
env_init.py generating ORES_HTTP_SERVER_JWT_SECRET without recording a new
.env format version, so no checkout was ever told to regenerate and every one of
them kept a line that make_mapper("HTTP_SERVER") maps onto an option no parser
registers — which stops ores.http.server at its next start. Version 26 records
the removal, and this checkout's .env was regenerated and the gateway restarted
to confirm.
Verified locally, since CI runs no full build: the whole tree builds, the
reporting, shell-reporting, workflow core and api, refdata api and reporting
service suites all pass, compass lint and the structure-note link check are
clean, the schema validator reports no warnings, and the reporting component has
no codegen drift.