From ccf83d5bc917c9d2bf1219533840e2ffaba69523 Mon Sep 17 00:00:00 2001 From: Mark Schaller Date: Mon, 2 Nov 2026 10:32:01 -0500 Subject: [PATCH] Check bazel_args with Bazel's own flag parser bazel_args is meant for the flags that make aquery mirror the real build (--config=release, -c opt). Anything that consumes it splices it into a bazel command line, but nothing checked its shape, and the SKILL.md text sits next to a literal `bazel aquery ...` line that is easy to paste in. Measured in any2bazel-lab: on a QGIS migration the agent recorded the whole aquery command as bazel_args. Its own diff never read the field, so the migration looked converged, while a consumer that spliced the field into `bazel aquery` failed on a second query expression and produced no parity result. SKILL.md now says the field holds flags, with one right and one wrong example, and runs `bazel canonicalize-flags --for_command=aquery` on it just before aquery. Bazel refuses anything that is not an aquery option, including a target pattern and a stray word after a boolean flag (`--keep_going true`), which a hand-written check would have to special-case. The one-liner hands the list to Bazel as argv (os.execvp), so a value with a space in it is not split. It does not check that a --config exists; aquery fails on one that does not. Checked against Bazel 9.3.1: it refused a whole aquery command and `--keep_going true`, and accepted `--copt=-DMSG="a b"`, `-c opt`, `--config=release` and a config with no bazel_args. The lab's later QGIS run, made with an earlier version of this change (a Python check), recorded only valid flags; this version has not been through a migration run. Lab-Proposal: P22 Co-Authored-By: Claude Opus 5.5 --- SKILL.md | 11 ++++++++--- scripts/config.py | 8 +++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/SKILL.md b/SKILL.md index 62ad5c0..d841c8b 100644 --- a/SKILL.md +++ b/SKILL.md @@ -112,9 +112,12 @@ whether its frontend reads it: ``` Fields: -- **`bazel_args`** — the extra args aquery must run with to mirror the real - build (see [Extract the Bazel action graph](#extract-bazel-graph)). Recorded so - the comparison is reproducible; read by the operator, not the diff. +- **`bazel_args`** — the extra **flags** aquery must run with to mirror the + real build (see [Extract the Bazel action graph](#extract-bazel-graph)), e.g. + `["--config=release"]` or `["-c", "opt"]`; never a command + (`["aquery", "mnemonic(...)", ...]` is wrong). Recorded so the comparison is + reproducible; check it with Bazel before running aquery (see + [Extract the Bazel action graph](#extract-bazel-graph)). - **`target_map`** — `reference_name → bazel_name` for intentionally renamed **executables**. Bazel targets are keyed by full label, so the value is usually `:name` (e.g. `"bssl": ":bssl"`). Libraries need no mapping. @@ -261,6 +264,8 @@ instead of being a warning nobody reads. > always point to files that are either stale, or do not exist at all. ```bash +# bazel_args, as Bazel parses them; it refuses anything that is not an aquery flag +python3 -c 'import json,os,sys; os.execvp("bazel", ["bazel", "canonicalize-flags", "--for_command=aquery", "--", *json.load(open(sys.argv[1])).get("bazel_args", [])])' any2bazel.json bazel aquery 'mnemonic("CppCompile|ObjcCompile|CppLink|CppArchive", //...)' \ --features=-compiler_param_file --features=-linker_param_file \ [--config=] [--copt=... --cxxopt=...] \ diff --git a/scripts/config.py b/scripts/config.py index fd17cbd..cd52739 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -100,9 +100,11 @@ class MigrationConfig: # Unlike `ignore` (flags/defines), this is the only lever for missing_tu / # missing_target on whole subtrees. Each entry is a reviewable decision. exclude_targets: Set[str] = field(default_factory=set) - # The bazel aquery invocation this migration is compared against, recorded - # so the comparison is reproducible. Read by the skill/operator, not by the - # diff itself (the diff consumes the already-extracted model). + # The FLAGS appended to `bazel aquery` so the Bazel side mirrors the real + # build (never the command itself), recorded so the comparison is + # reproducible. Read by the skill/operator, not by the diff itself (the + # diff consumes the already-extracted model); SKILL.md has Bazel parse + # them before aquery. bazel_args: tuple = () # Opt-in: also diff TEST targets (role=test). OFF by default because test # diffing requires BOTH models to be extracted with tests enabled and the