# Changelog All notable changes to cargoxx will be documented in this file. ## [Unreleased] ### Added - M0 repo skeleton: hand-written `CMakeLists.txt`, bootstrap `flake.nix`, empty C++23 module units for every component listed in `TECH_SPEC.md` §1 (`util`, `exec`, `manifest`, `lockfile`, `layout`, `linkdb`, `resolver`, `codegen`, `cli`), root module `cargoxx`, and a stub `main.cpp` that builds an empty `cargoxx` binary. - `.clang-format` (LLVM, 100-column) and `.gitignore`. - `SPEC.md`, `TECH_SPEC.md`. - M1 foundation in `cargoxx.util`: `ErrorCode` (numbers per `TECH_SPEC.md` §4), `Error`, `Result = std::expected`, and `format(Error)` rendering `error[Ennnn]: ...` with optional `--> path:line:col` and `hint:` lines. `format` lives in `src/util/error.cpp` as a module implementation unit. - `Catch2 v3` wired through `flake.nix` (libc++-built override) and registered in CMake; `tests/util_error.cpp` covers six format cases via `catch_discover_tests`. - `cargoxx.manifest` public types (`Package`, `Dependency`, `BuildSettings`, `Edition`, `Manifest`) and `parse(path)` returning `Result`, backed by toml++ vendored as `third_party/toml.hpp`. Unknown keys in `[package]` / `[build]` and unknown top-level keys are rejected; reserved fields (`description`, `repository`, `[dev-dependencies]`, `[features]`, `[workspace]`) are accepted. `tests/manifest_parse.cpp` covers 17 cases. - `manifest::write(m, path)` serializes a `Manifest` as TOML using toml++. Dependencies are emitted alphabetically (matches Cargo). Round-trip property is exercised by `tests/manifest_write.cpp` (9 cases). Defaulted `operator==` on the manifest structs supports comparison. - `cargoxx.layout` public types (`Target`, `TargetKind`, `DiscoveredLayout`) and `discover(project_root, package_name)`. Walks `src/` recursively for the library (excluding the `src/bin/` subtree), enumerates `src/main.cpp`, `src/bin/*.cpp`, `tests/*.cpp`, `examples/*.cpp` flat, sorts results for deterministic output, and returns `LayoutNoTarget` when neither a library nor any binary is present. `tests/layout_discovery.cpp` covers 12 cases. - `cargoxx new ` and `cargoxx new --lib ` scaffold a project directory with `Cargoxx.toml`, `src/main.cpp` or `src/lib.cppm`, and a minimal `.gitignore`. Hyphens in the package name are mapped to underscores when generating the module/namespace identifier. Codegen of `flake.nix` / `CMakeLists.txt` is intentionally deferred to M3. CLI11 v2.6.2 vendored at `third_party/CLI11.hpp`; entry point is now `cargoxx::cli::run(argc, argv)`. `tests/cmd_new.cpp` covers 9 cases. - `util::satisfies(version, range)` — minimal semver range matcher supporting `*`, `==/>=/<=/>/<` operators, and comma-separated AND clauses. Versions are zero-padded to three components. `tests/semver_satisfies.cpp` covers 7 cases. - `cargoxx.linkdb`: `Recipe`, `Database::open()`, `Database::resolve(...)` for curated lookups. The curated database ships at `data/linkdb.json` with all 25 packages from `SPEC.md` §11. Component-substitution helpers expand `{{components}}` (space-joined) in `find_package` and fan out `{{component}}` per-target. Path is injected via the `CARGOXX_LINKDB_DEFAULT_PATH` compile definition. nlohmann/json 3.12.0 vendored at `third_party/json.hpp`. SQLite overlay and `add_manual` are deferred to the M2 follow-up commit. `tests/linkdb_lookup.cpp` covers 13 cases including a smoke test that resolves all 25 curated packages. - `cargoxx.lockfile`: `Lockfile`, `LockfilePackage` types and `parse(path)` / `write(lock, path)` matching the format in `SPEC.md` §5. Also `Lockfile::nixpkgs_rev()` returns the shared revision (codegen will consume this in M3). `tests/lockfile_round_trip.cpp` covers 9 cases. - `cargoxx.codegen`: `GenerateInputs` plus the pure function `flake_nix(in) -> std::string`. Substitutes the package name, the resolved nixpkgs revision (defaulting to `nixos-unstable` when the lockfile pins none), and a deduplicated list of dep `nixpkgs_attr` entries into `buildInputs`. Output is byte-deterministic. `tests/codegen_flake.cpp` covers 7 cases. Note: SPEC §7's template did not show `buildInputs`; we add one between `nativeBuildInputs` and the `env.NIX_CFLAGS_COMPILE` block as the natural slot for the deps that TECH_SPEC §10 says we splice in. - `codegen::cmake_lists(in) -> std::string` per `SPEC.md` §8 and `TECH_SPEC.md` §9: `find_package` per dep, optional library target with module units + private impl sources, primary binary `_bin` linked against the library, additional binaries from `src/bin/`, tests with `enable_testing()` + `add_test`, examples, and `[build]` honoring `warnings_as_errors` and `sanitizers`. Source paths emitted relative to `build/` (i.e. prefixed with `../`). Output is deterministic. `tests/codegen_cmake.cpp` covers 11 cases. - `cargoxx.resolver::devbox_resolve(name, version)` and pure parser `parse_devbox_resolve(json)` — port of devbox's `internal/searcher/client.go` Resolve method. Hits `https://search.devbox.sh/v1/resolve?name=&version=` via curl and pulls out `name`, `version`, `commit_hash`, `attr_paths`. Falls back to the per-system `systems..commit_hash` when the top-level field is empty (older response shapes). 404 → `ResolutionUnknownPackage`, missing commit_hash → `ResolutionVersionNotFound`, transport / parse errors → `ResolutionNetworkError`. `tests/devbox_resolve_parse.cpp` covers 6 cases against fixtures derived from a real fmt 10.2.1 response; `tests/devbox_resolve_live.cpp` (gated by `CARGOXX_NETWORK_TESTS=1`) hits the live API. - Fix: `nix_cmake_scan` would silently fail when the dev output had not yet been realized on the local machine — `nixpkgs_probe`'s `nix eval` only *computes* a store path, so packages first-time encountered (`libclang`, anything not previously built) returned a path that didn't exist on disk. Discovery now realizes the candidate output via `nix build --no-link --print-out-paths nixpkgs#.dev` (or `.`) before scanning. New free function `cargoxx::resolver::realize_path(flake_attr)` wraps the call. Live verified: `cargoxx add libclang` now reaches the verify-link step (the subsequent `find_dependency(LLVM)` failure inside Clang's CMake config is the pre-existing cross-package transitive-dep limit — workaround is to `cargoxx add libllvm` first). - Fix: `nix_cmake_scan` walked only the package's default output. For multi-output Nix packages — `boost`, `openssl`, `libllvm`, `libclang`, glib, … — CMake configs live in the `dev` output (`/lib/cmake/llvm/LLVMConfig.cmake`), not under the default output, so `cargoxx add libllvm` (or any other multi-output dep) failed with "Conan/vcpkg/nix-scan all empty". `NixpkgsInfo` now carries an optional `dev_path`; the `nix eval` apply expression reports `if p ? dev then p.dev.outPath else ""`. `discover` scans the dev output preferentially and falls back to `out`. Live verified: `cargoxx add libllvm` now reaches the verify-link step (the linker-time libstdc++/libc++ ABI mismatch is a separate documented limit and applies to abseil-cpp too). - Fix: pinned-dep `buildInputs` line used the curated linkdb's `nixpkgs_attr` (e.g. `fmt_10`) regardless of the pinned rev, so `cargoxx add fmt@12.1.0` emitted `pkgs_nixpkgs_fmt_12_1_0.fmt_10` even though that rev only exposes `fmt`. `resolve_version` now returns `ResolvedVersion { nixpkgs_rev, nixpkgs_attr }` (the attr comes from devbox's `attr_paths` for the resolved rev, with the package name as a best-effort fallback for the git path). `cmd_add` writes the attr into the lockfile and `cmd_build`'s `merge_lockfile` now also preserves it. Codegen prefers the lockfile's attr over the linkdb recipe's whenever it's set, so pinned deps reach the right attribute on their per-dep nixpkgs. Live verified: `cargoxx add fmt@12.1.0 && cargoxx build` now emits `pkgs_nixpkgs_fmt_12_1_0.fmt` and the binary builds and runs. - `flake.nix` codegen rewritten for **per-dep nixpkgs revisions**. The shared `nixpkgs` input always tracks `nixos-unstable` and provides the toolchain. Each pinned manifest dep (`@` whose lockfile entry has a non-null `nixpkgs_rev`) gets its own `nixpkgs_` input, a matching outputs-lambda parameter, a `pkgs_nixpkgs_` `let` binding, and a `pkgs_nixpkgs_.` line in `buildInputs`. Wildcard / unpinned deps stay on the shared `pkgs.`. Sanitization replaces every char outside `[a-zA-Z0-9_]` with `_`, giving a single identifier-safe form for all three Nix contexts. Two pinned deps that share the same `(name, version)` are deduplicated. Live verified: `cargoxx new app && cargoxx add fmt@10.2.1 && cargoxx add zlib && cargoxx build` produces a binary that calls fmt 10.2.1 + zlib from the toolchain nixpkgs; a second `cargoxx build` regenerates byte-identical `flake.nix` + `Cargoxx.lock`. - SPEC.md §7 (flake template) and §10 (version resolution) amended to describe the per-dep model. The previous single-shared-rev whole-project SAT was retired (user-directed; documented ABI trade-off). - `cargoxx build` now **merges** rather than overwrites `Cargoxx.lock`: when an existing entry's `(name, version)` still matches the manifest, its `nixpkgs_rev` is preserved. New deps and version bumps still get a null rev (track `nixos-unstable`). Means pins written by `cargoxx add @` survive arbitrary rebuilds; cmd_build is now idempotent w.r.t. the lockfile. `synthesize_lockfile` was renamed `merge_lockfile`. - `cargoxx.resolver::resolve_version(name, version)` orchestrator chains `devbox_resolve` (HTTP, primary) → `nixpkgs_git_resolve` (offline, fallback). Returns a 40-char nixpkgs SHA. Wildcards (`*`, empty) are rejected with `ResolutionVersionNotFound` since they aren't concrete pins. - `cargoxx add @` now resolves the pin to a nixpkgs rev *before* writing the manifest. Failure to resolve aborts the add cleanly (no manifest mutation, no lockfile mutation). On success the rev is persisted into `Cargoxx.lock`'s `nixpkgs_rev` for that dep. Wildcards (`cargoxx add fmt`) skip resolution and track `nixos-unstable` as before. Tests opt out of the network resolver via `CARGOXX_NO_AUTORESOLVE=1`. - `cargoxx.resolver::nixpkgs_git_resolve(name, version, repo_path?)` — fallback for when search.devbox.sh is unreachable. Lazily clones `https://github.com/NixOS/nixpkgs.git` into `$XDG_CACHE_HOME/cargoxx/nixpkgs/` (or `$HOME/.cache/...`) on first use, then runs `git -C log --all -S 'version = ""' --pretty='%H %ct' -- pkgs/` and returns the youngest matching commit. Pure helper `pick_youngest_commit(text)` parses the `%H %ct` lines. The unit test builds a tiny throwaway git fixture that mirrors the `pkgs/development/libraries//default.nix` layout (avoiding the real multi-GB clone) and verifies introducing-commit detection plus the not-found path; 5 cases. - `cargoxx add ` now auto-resolves packages outside the curated linkdb. On `LinkdbUnknownPackage`, `cmd_add` invokes `resolver::discover` which: probes `nixpkgs#` to confirm the attribute exists and capture its store path; tries Conan, vcpkg, and the nix-store CMake scan in order; for each candidate runs a real `cargoxx build` against an empty `int main() {}` project via `verify_link`; on first success persists a confirmed overlay row. Subsequent `cargoxx add ` for the same package is an overlay-cache hit (~ms). End-to-end live: a fresh `cargoxx add simdjson` (not in our curated db) takes ~11 s including the verifying nix+cmake build, then 0.002 s on the second invocation. `linkdb::default_overlay_path()` is now exported as a public helper so the resolver and CLI agree on the overlay file when no path is explicitly passed. Tests opt out of the slow chain via `CARGOXX_NO_AUTORESOLVE=1`. - `cargoxx.linkdb::Database::insert_provisional`, `confirm_provisional`, and `abort_provisional` — three-step lifecycle for non-`manual` overlay rows: insert with `verified_at = 0`, run a build, then either bump `verified_at = now` on success or `DELETE` the row on failure. - `cargoxx.resolver::verify_link(req, build_fn)` — scaffolds a tiny `Cargoxx.toml` + `int main() {}` project under a scratch dir, writes a provisional overlay row pointing at the candidate `Recipe`, runs the caller-supplied `BuildFn` (typically `cli::cmd_build` injected to avoid a resolver-on-cli dep cycle), and on success/failure confirms or aborts the provisional row. Cleans the scratch project via RAII regardless of outcome. `tests/verify_link_unit.cpp` exercises both success and failure paths against a mock `BuildFn`, verifying that the overlay row's lifecycle matches the build outcome. - `cargoxx.resolver::vcpkg_probe(name)` — fetches `https://raw.githubusercontent.com/microsoft/vcpkg/master/ports//usage` and feeds it through `parse_vcpkg_usage`. The pure parser extracts the first `find_package(...)` arg block, adds `REQUIRED` if absent, and gathers `target_link_libraries` targets that contain `::` (skips generator expressions and bare names). `tests/vcpkg_probe_parse.cpp` covers 6 cases; `tests/vcpkg_probe_live.cpp` (gated by `CARGOXX_NETWORK_TESTS=1`) verifies fmt + a 404 path against real microsoft/vcpkg ports. - `cargoxx.resolver::conan_probe(name)` — fetches `https://raw.githubusercontent.com/conan-io/conan-center-index/master/recipes//all/conanfile.py` via `curl` (text-only — never executes Python, per `SPEC.md` §14) and feeds it through `parse_conanfile`. Pure parser handles both the modern `set_property("cmake_target_name", "...")` form and the legacy `names["cmake_find_package"] = "..."` form, derives the missing field from the other when only one is set, normalizes bare names into the canonical `::` shape, and substitutes Python f-string `{...}` placeholders with the cmake_file_name so recipes like fmt's `f"fmt::{target}"` parse correctly. `tests/conan_probe_parse.cpp` covers 6 cases; `tests/conan_probe_live.cpp` (gated by `CARGOXX_NETWORK_TESTS=1`) verifies fmt and a 404 path against the real conan-center-index. - `cargoxx.resolver::nix_cmake_scan(store_path, package_name)` — walks `/lib/cmake/**` for `*Config.cmake` / `*-config.cmake` files, scans them and their sibling `.cmake` files (e.g. the `-targets.cmake` that real packages like fmt put their `add_library(... IMPORTED)` calls in) for IMPORTED + ALIAS targets, and returns the `NixCmakeCandidate` whose derived stem best matches `package_name` (case-insensitive equality > prefix > first non-empty). Pure helpers `scan_imported_targets(text)` and `config_stem_to_package(filename)` are exported for unit testing. `tests/nix_cmake_scan_parse.cpp` covers 11 cases including a fixture that mirrors fmt's `-config.cmake → -targets.cmake` layout. `tests/nix_cmake_scan_live.cpp` (gated by `CARGOXX_NETWORK_TESTS=1`) realizes `nixpkgs#fmt.dev` via `nix build` and verifies `fmt::fmt` is discovered end-to-end. - `cargoxx.resolver::nixpkgs_probe(attr)` — runs `nix eval nixpkgs# --json --apply 'p: { version, path }'` via `exec::run` and returns a `NixpkgsInfo { attr, version, out_path }`. Distinguishes missing attributes (`ResolutionUnknownPackage`) from evaluator/network errors (`ResolutionNetworkError`). The `path` field name avoids nix's `outPath`-driven derivation coercion in `--json` mode (which would otherwise serialize the result as a bare path string instead of a JSON object). Pure parser `parse_nix_eval_json(attr, text)` exposed for unit tests; live tests in `tests/nixpkgs_probe_live.cpp` are gated behind `CARGOXX_NETWORK_TESTS=1` (verified locally against `hello`). - `cargoxx add [@] [--components a,b]` edits `Cargoxx.toml`, validates the new dep against the curated linkdb (so unknown packages and missing components fail before any disk write), and rejects already-declared deps. Version is now optional; an omitted version is stored as `"*"` and resolves against the linkdb's first matching recipe. Generated `flake.nix` continues to track the `nixos-unstable` branch when the lockfile carries no rev. - `util::satisfies` treats `version == "*"` as a match against any range, mirroring the existing `range == "*"` shortcut. - `scripts/verify-curated-db.sh` plus per-package using-snippets at `scripts/curated-snippets/.cpp`. For each curated package the script scaffolds a fresh project, `cargoxx add`s the dep, drops in a snippet that exercises the library's API, and runs `cargoxx build`, asserting the binary is produced. 21/25 packages pass against `nixos-unstable`; the remaining 4 are documented as skipped: `catch2`/`gtest` (their default linkdb targets ship their own `main()` which collides with the snippet), `grpc` (needs `protobuf` declared as a transitive dep — cross-package transitives are out of v0.1 scope), `abseil-cpp` (nixpkgs builds it against libstdc++ while our flake uses libc++ → ABI mismatch on the linker step). - `data/linkdb.json`: `boost` recipe is now header-only — `find_package(Boost REQUIRED)` + `Boost::headers`. Boost 1.89's `BoostConfig.cmake` searches for separately-installed `boost_Config.cmake` files that nixpkgs doesn't ship, so the previous `COMPONENTS` form failed at configure time. Linkdb-component tests now use `abseil-cpp` for component coverage. - `cargoxx remove ` drops a declared dep from `Cargoxx.toml`, errors when the dep is not declared. Other deps are preserved. - End-to-end verified on a freshly-scaffolded project. `tests/cmd_add.cpp` and `tests/cmd_remove.cpp` cover 9 cases. Known cosmetic issue: toml++ writes top-level sections alphabetically, so `[package]` may end up after `[dependencies]` post-mutation; logged for M6 polish. - `cargoxx run [--release] [--bin ] [-- ...]`, `cargoxx test [--release]`, and `cargoxx clean`. `run` builds first, picks the binary (errors with the available list when the project has multiple bins and `--bin` is omitted), and execs it with the process exit code propagated. `test` invokes `nix develop -c ctest --test-dir build/ --output-on-failure`. `clean` removes `build/` while leaving `Cargoxx.lock` and `flake.lock` intact. End-to-end verified against a freshly-scaffolded project. `tests/cmd_run.cpp` and `tests/cmd_clean.cpp` cover 6 cases. - `cargoxx build` (without `--no-build`) now drives the full build: `nix develop -c cmake -B build/ -S build -G Ninja -DCMAKE_BUILD_TYPE=` then `nix develop -c cmake --build` with optional `--target`, both with `inherit_stdio = true` so the user sees compilation output live. Non-zero cmake exit returns `BuildCmakeFailed`. End-to-end verified: `cargoxx new myapp && cargoxx build` produces a working `build/debug/myapp` that prints `Hello from myapp!`. - Generated `build/CMakeLists.txt` now opts into the experimental `import std;` UUID + `CMAKE_CXX_MODULE_STD ON`, and sets `CMAKE_CXX_EXTENSIONS ON` (deviation from SPEC §8 — required for libc++'s std module to load without `module-file-config-mismatch` on clang 21). Without these the templates from `cargoxx new` fail to compile. - `cargoxx.exec`: `ExecResult`, `ExecOptions`, and `run(program, args, opts)` — argv-only subprocess wrapper around reproc 14.2.4. Captures stdout/stderr (or inherits stdio when `opts.inherit_stdio` is set), supports `cwd`, `env_overrides`, and a `timeout` (enforced via `reproc_options.deadline` so drains return `REPROC_ETIMEDOUT` instead of blocking on long-lived streams). On destruction reproc terminates and then kills any still-running child. Returns `ExecToolNotFound` for `ENOENT` and `ExecCommandFailed` for other failures including timeouts. `tests/exec_run.cpp` covers 9 cases. - `cargoxx build --no-build` end-to-end. Reads `Cargoxx.toml`, discovers the layout, opens the curated linkdb, resolves a `Recipe` per manifest dep, synthesizes a fresh `Cargoxx.lock`, and writes `flake.nix`, `build/CMakeLists.txt`, and `Cargoxx.lock`. With no resolver yet (M5), `nixpkgs_rev` is left null and the generated flake falls back to the `nixos-unstable` branch. Without `--no-build`, the command still generates files but returns a `NotImplemented` error pointing at M4. `tests/cmd_build.cpp` covers 8 cases. - SQLite overlay: `Database::open(overlay_path)` now opens (and creates, if missing) a per-user `linkdb.sqlite` cache, applying the schema from `SPEC.md` §9 idempotently. Default path is `$XDG_CACHE_HOME/cargoxx/linkdb.sqlite` (falling back to `$HOME/.cache/cargoxx/...`); tests pass an explicit temp path so they never touch the user cache. `Database::add_manual(pkg, range, recipe)` inserts a row with `source = 'manual'` and `verified_at = now()`; `resolve()` consults the overlay first and falls back to curated when no overlay row matches the requested version range. Manual entries never expire; `nix-probe` (v0.2) entries respect a 30-day freshness window. `tests/linkdb_overlay.cpp` covers 7 cases (insert/persist, override-curated, version-range gating, components rejection, move semantics). - M7 generated flake.nix moves to `build/flake.nix`. The project root belongs to the user — any hand-written `flake.nix` there is never overwritten by cargoxx. `cargoxx build` always invokes `nix develop` against `path:./build`. - M7 lockfile pins top-level `nixpkgs_rev` and `flake_utils_rev`. The generated flake's `inputs.nixpkgs.url` / `inputs.flake-utils.url` now use the pinned revs (falling back to the branch tips during the transitional first build before the lock is written). Per-package schema gains the full recipe (`find_package`, `targets`, `pkg_config_module`, `brute_force_libs`, `brute_force_includes`, `linkdb_source`) so the lockfile is a complete dependency-pinning artifact and `cmd_build`'s `recipe_from_lock` can short-circuit the linkdb entirely. `tests/lockfile_round_trip.cpp` extended. - M7 `codegen::VendorIndex` + `parse_vendor_toml` — new pure parser (`src/codegen/vendor.cpp`) returns a struct of `nixpkgs_store_path`, `flake_utils_store_path`, and a per-dep `nixpkgs_attr → store_path` map. `GenerateInputs` gains an optional `vendor` field; when set, `emit_inputs_block` emits `path:` inputs and drops the per-dep `github:` pins. - M7 new helpers in `cargoxx.resolver`: `realize_path_at_rev(rev, attr)` realizes `github:NixOS/nixpkgs/#` to a `/nix/store/...` path (used by `cmd_vendor`); `realize_flake_source(flake_ref)` returns the source store path via `nix flake prefetch --json` (used to pin `nixpkgs` and `flake-utils` for offline mode). - M7 `cargoxx vendor [--output ]` — new CLI verb. Reads `Cargoxx.lock`, realizes each locked dep at its pinned `(nixpkgs_rev, nixpkgs_attr)` into `/nix/store`, and writes `vendor.toml` (schema = 1) recording the resolved store paths for every dep plus the `nixpkgs` and `flake-utils` flake sources. The output is the input to `cargoxx build --offline`. - M7 `cargoxx build --offline [--vendor ]` — skips every network probe (Conan/vcpkg fuzzy, devbox, nixpkgs_git, linkdb auto-resolve), reads `vendor.toml` (default `./vendor.toml`), and emits `build/flake.nix` with literal `path:/nix/store/...` inputs for `nixpkgs`, `flake-utils`, and every dep. Offline mode also runs cmake directly (no outer `nix develop` wrapper) since all paths are already realized in the local store. - M7 `cargoxx.lib.buildCppPackage` — hermetic, sandbox-safe nix builder for downstream flakes. Mirrors `rustPlatform.buildRustPackage`'s ergonomics: a consumer flake passes `src` and gets a derivation. Reads `Cargoxx.lock` at outer eval time, resolves each dep's `(nixpkgs_rev, nixpkgs_attr)` via `builtins.getFlake` into concrete `/nix/store/...` paths, and synthesizes a `vendor.toml` via `pkgs.writeText` — no network or nested `nix` invocations inside any build phase. The single derivation runs `cargoxx build --release --offline --vendor /vendor.toml`, which emits a hermetic `build/flake.nix` with literal `path:/nix/store/...` inputs and drives cmake directly. Works under the host's default sandbox (sandbox=true, non-trusted user, no `__noChroot`, no daemon escape). New e2e fixture at `tests/e2e/buildCppPackage/` with a `run.sh` smoke test that scaffolds the fixture in a tmp dir and runs `nix build .#default` end-to-end. Live verified: `Hello from world!` from a binary built entirely inside the standard nix sandbox. - Fix: `-Wparentheses` warning in `looks_like_missing_attribute` (`src/resolver/nixpkgs_probe.cpp:34`) — explicitly parenthesize the `&&` clause inside `||`.