feat: add tools/benchmarks list, --aggregation, and debug log-level to integration tests - #225
Conversation
Adds smoke coverage for the new discovery commands (crucible#655), gated on CI_RELEASE the same way as the neighboring 'crucible userenvs' block since no existing quarterly release has them yet. Beyond bare exit-code checks, also verify with jq that the JSON output is well-formed and its tools/benchmarks array is non-empty -- a broken aggregation could otherwise silently return an empty list and still exit 0, which is exactly the class of bug found and fixed across two review rounds on crucible#655.
Adds correctness coverage for crucible get metric's --aggregation sum|avg|max|min override, beyond a bare exit-code check: confirms omitting --aggregation reproduces the same value as the metric's stored default-aggregation (sum, for mpstat/Busy-CPU), that min <= avg <= max holds, and that sum != avg specifically -- the last check exists because min<=avg<=max holds trivially if all four values come back identical, which would otherwise let a "flag is silently ignored" regression pass undetected. Gated on CI_RELEASE the same way as the tools/benchmarks list block: the --aggregation feature landed in CommonDataModel commit 34539bb (2026-07-30), which postdates even the current 2026.3 release branch (confirmed via git merge-base --is-ancestor). Also found, and filed separately (CommonDataModel#207), that --aggregation has no validation against the target metric's class -- out of scope for this test, which only exercises the documented, supported behavior.
Adds a new --ci-log-level option (default "debug") and applies it via --log-level to "crucible run" invocations, so a CI failure has enough detail (module/function/line-number annotated messages) to debug without needing to reproduce it manually. Deliberately "debug", not "verbose-debug" -- the latter also cranks roadblock's own protocol tracing to its most verbose mode end-to-end, useful when actively chasing a roadblock-specific issue but too noisy to run unconditionally on every CI run. --log-level on "crucible run" itself only landed in the 2026.3 release, so it's only threaded through the "run-file" (--from-file) parameter mode, which runs unconditionally across every release in the CI matrix. The "mv-params" legacy CLI mode is separately restricted (CI_MV_PARAMS_SUPPORTED) to releases old enough that --log-level was never a thing there in the first place, so it's correctly left untouched.
--from-file has been a silently-discarded no-op on "crucible run" since crucible commit 7d11fa7 (2025-09-24, "remove the legacy run CLI") -- confirmed via git merge-base that this predates every release currently in the CI matrix (2025.4, 2026.1, 2026.2, 2026.3) as well as upstream, so it's never actually necessary here. Verified empirically with CRUCIBLE_DRY_RUN that "crucible run <file> --log-level debug" and "crucible run --from-file <file> --log-level debug" produce byte-for-byte identical rickshaw-run.py invocations.
PR Review: crucible-ci#225 — feat: add tools/benchmarks list, --aggregation, and debug log-level to integration testsSummary: Adds CI coverage for Bugs
File Coverage
Limitations
VerdictApprove with comments — the stale-data bug in the 🤖 Generated with Claude Code |
…ed result The five aggregation_*_value variables were plain globals never reset between period-loop iterations, and the final consistency check wasn't guarded on RC_STATUS. If an earlier command in a later period failed, the aggregation run_and_capture_cmd calls silently no-op'd, leaving stale values from a prior period's successful check -- which the final check then reported as "verified" even though none of that period's queries actually ran. Reset the five values before each attempt and gate the final verify/error message on RC_STATUS == 0, matching the guard already used by the tools/benchmarks list check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Fixed in d2350b4. The five 🤖 Generated with Claude Code |
atheurer
left a comment
There was a problem hiding this comment.
Pull Request Review: crucible-ci PR #225
I have performed an in-depth review of PR #225 on the crucible-ci repository. The PR is a high-quality contribution that significantly improves the integration testing capabilities of the CI pipeline by adding correctness assertions, log levels for better debuggability, and new command coverage.
Below is a detailed analysis of the changes, design decisions, and logical verification.
1. Summary of Changes
The PR modifies a single file: .github/actions/integration-tests/run-ci-stage1. The changes can be grouped into four primary logical blocks:
-
Detailed Logging in CI (
--ci-log-level/--log-level)- Introduces a new CI parameter
--ci-log-level(defaulting to"debug"). - Dynamically threads this as
--log-leveltocrucible runinvocations for releases2026.3and newer (includingupstream). - Old releases (up to
2026.2) that do not support--log-levelare correctly filtered out to prevent execution crashes.
- Introduces a new CI parameter
-
Removal of Deprecated
--from-fileCLI Flag- Replaces the deprecated and ignored
--from-fileoption with direct positional file passing:-run_cmd "crucible run --from-file ${CI_RUN_FILE}" +run_cmd "crucible run ${CI_RUN_FILE} ${CI_LOG_LEVEL_ARG}"
- This prevents potential future breakage or deprecation errors while preserving backward compatibility.
- Replaces the deprecated and ignored
-
Metric Aggregation Override Correctness Verification
- Adds correctness assertions for
crucible get metric --aggregation <sum|avg|min|max>on supported releases (newer than2026.3). - Ensures that the default aggregation matches
sum(the schema default formpstat/Busy-CPU). - Mathematically verifies that
min <= avg <= maxholds true. - Asserts that
sum != avgto verify that aggregation is actually occurring and not being silently ignored (which would cause all outputs to look identical and bypass the inequality assertions). - Resets state variables (
aggregation_*_value) inside the period loop to avoid stale-value leakage and guards checks underRC_STATUS == 0.
- Adds correctness assertions for
-
Discovery Command Coverage (
crucible tools list&crucible benchmarks list)- Adds integration test coverage for the new listing commands introduced in crucible
#655. - Validates not only the zero exit codes but also parses the JSON output with
jqto confirm they contain at least one tool/benchmark item and are well-formed.
- Adds integration test coverage for the new listing commands introduced in crucible
2. Technical Evaluation & Logic Verification
A. Logging and Release Gating
The release gates are accurately mapped:
--log-level: Only introduced in2026.3.case "${CI_RELEASE}" in "2024.4"|"2025.1"|"2025.2"|"2025.3"|"2025.4"|"2026.1"|"2026.2") # Correctly skips older releases
--aggregationoverride and listing commands: Only introduced after2026.3.case "${CI_RELEASE}" in "2024.4"|"2025.1"|"2025.2"|"2025.3"|"2025.4"|"2026.1"|"2026.2"|"2026.3") # Correctly skips 2026.3 and older
This categorization is accurate and prevents command execution failure on older, pinned branches during backward-compatibility tests.
B. Defensive Scripting and Variable Isolation
In the final revision of the PR, the aggregation variables are properly reset on every period loop iteration:
aggregation_default_value=""
aggregation_sum_value=""
aggregation_avg_value=""
aggregation_max_value=""
aggregation_min_value=""This is critical because if an earlier command fails in a subsequent period, run_and_capture_cmd will skip execution (leaving the variables unchanged), but the final check will now be correctly skipped because it is gated behind if [ ${RC_STATUS} == 0 ].
Furthermore, extracting the values with sed -n '/^{/,$p' is highly robust as it strips any leading header lines/roadblock notices before passing the clean JSON to jq.
C. Mathematical Comparison Logic via awk
Since bash does not support floating-point mathematics natively, using awk to perform float comparisons is excellent practice:
awk -v a="${aggregation_min_value}" -v b="${aggregation_avg_value}" 'BEGIN { exit !(a <= b) }'This evaluates accurately and sets the correct bash exit status (0 for true, 1 for false).
The check sum != avg is particularly clever because it ensures the aggregation flag is not simply being ignored server-side. For mpstat/Busy-CPU, there are 7 subtypes, making the sum always significantly greater than the average.
3. Potential Enhancements & Minor Recommendations
While the PR is exceptionally well-engineered, here are a few minor recommendations to consider:
Recommendation 1: Handling potential empty/non-numeric values in awk comparisons
If one of the metrics fails to capture a numeric value, or if jq returns an empty string or null, the awk comparison might face syntax or type issues.
While the script guards against empty strings via [ -n "${variable}" ], a non-numeric string (e.g. "null" or "error") could still pass the -n check but fail in the awk comparison.
A small sanitizer or regex match to ensure the values are indeed numeric before passing to awk would make it bulletproof:
if [[ "${aggregation_default_value}" =~ ^[0-9.-]+$ ]] && ...Recommendation 2: Quotation of CI_LOG_LEVEL_ARG in run_cmd Invocations
In run-ci-stage1, the crucible run command is invoked as:
run_cmd "crucible run ${CI_RUN_FILE} ${CI_LOG_LEVEL_ARG}"Since run_cmd executes its first argument unquoted (e.g. ${cmd} on line 112 of common-code.sh), this unquoted expansion splits on whitespaces, which works perfectly here:
- If
CI_LOG_LEVEL_ARGis"--log-level debug", it splits into two arguments. - If
CI_LOG_LEVEL_ARGis"", it splits into nothing (no empty argument passed to crucible).
This is correct and robust, but a developer reading the code might be tempted to quote ${CI_LOG_LEVEL_ARG} in the future (e.g., "${CI_LOG_LEVEL_ARG}"), which would cause crucible run to receive an empty-string argument and fail. Adding a quick inline comment explaining why it is unquoted could prevent future regressions.
4. Conclusion
This pull request is in excellent shape and ready to merge. The logical separation across the 5 commits is exemplary, and the defensive scripting strategies employed (such as variable resetting, release gating, and robust JSON stripping) make the CI pipeline exceptionally resilient.
Summary
crucible tools list/crucible benchmarks list(crucible#655): bare invocation,--format table|json,--name, and ajq-based check that the JSON output is well-formed and non-empty (not just exit-code-0) — this last check exists because a broken aggregation could otherwise silently return{"tools": []}and still exit 0, which is exactly the class of bug found and fixed across two review rounds on crucible#655.crucible get metric's--aggregation sum|avg|max|minoverride, usingmpstat/Busy-CPU(already queried elsewhere in this same test): confirms omitting--aggregationreproduces the metric's storeddefault-aggregation, thatmin <= avg <= maxholds, and thatsum != avgspecifically — the last check exists becausemin<=avg<=maxalone would pass even if--aggregationwere silently ignored server-side and all four values came back identical.--ci-log-leveloption (defaultdebug) applied to everycrucible run --from-file-equivalent invocation, so a CI failure has enough detail (module/function/line-number annotated messages) to debug without reproducing it manually. Deliberatelydebug, notverbose-debug— the latter also cranks roadblock's own protocol tracing to its most verbose mode end-to-end, useful when actively chasing a roadblock-specific issue but too noisy to run unconditionally on every CI run. Only threaded through the "run-file" parameter mode (which runs unconditionally across every release); the "mv-params" legacy CLI mode is separately restricted to releases old enough that--log-levelwas never a thing there.--from-filefromcrucible runinvocations (silently discarded as a no-op since crucible commit7d11fa7, predating every release in the CI matrix).All new/changed behavior is gated on
CI_RELEASEconsistent with this file's existing convention, verified viagit merge-base --is-ancestoragainst each feature's actual introducing commit rather than assumption:crucible tools list/benchmarks list: upstream-only (crucible#655, not yet in any release branch including2026.3)--aggregation: upstream-only (CommonDataModel commit34539bb, 2026-07-30, postdates2026.3)--log-leveloncrucible run: present in2026.3but not2025.4/2026.1/2026.2Test plan
bash -nsyntax-checked after every changemin<=avg<=max,sum!=avg) tested against synthetic good/regression/missing-data cases before landing--log-levelargument-order and word-splitting behavior verified empirically viaCRUCIBLE_DRY_RUN=1(old--from-fileorder vs. new positional order produce byte-for-byte identical constructedrickshaw-run.pycommands)crucible tools list/benchmarks listand the NOTICE/banner-stripping assumptions against the real CLI outputcrucible-ciworkflow will exercise all of the above for real)