diff --git a/spacecraft-qt-guidelines.skill b/spacecraft-qt-guidelines.skill index 4abb8f4..0825b02 100644 Binary files a/spacecraft-qt-guidelines.skill and b/spacecraft-qt-guidelines.skill differ diff --git a/spacecraft-qt-guidelines.zip b/spacecraft-qt-guidelines.zip index 2394adf..d645cb5 100644 Binary files a/spacecraft-qt-guidelines.zip and b/spacecraft-qt-guidelines.zip differ diff --git a/spacecraft-qt-guidelines/SKILL.md b/spacecraft-qt-guidelines/SKILL.md index 3a16772..f80c872 100644 --- a/spacecraft-qt-guidelines/SKILL.md +++ b/spacecraft-qt-guidelines/SKILL.md @@ -66,8 +66,8 @@ Always choose the abstraction corresponding to the task: 7. **Theme tokens only:** `QPalette` roles bound to `steelbore` tokens and a generated `steelbore.qss`; a bare hex literal is a §11.1 violation. ## Build, Tooling & CI (Non-Negotiable) -- **Toolchain floor:** Qt **6.8 LTS** (supported to 2029-10-08); Qt 6.11 is current, and 6.12 is the next LTS. CMake ≥ 3.21, C++20. CXX-Qt 0.9.x, Rust 1.83+. -- **CMake shape:** `qt_standard_project_setup()`, `qt_add_executable`, `qt_add_qml_module`. Never hand-rolled `moc` invocations; never `qmake` in new projects. +- **Toolchain floor:** Qt **6.8 LTS** (supported to 2029-10-08); Qt 6.11 is current and validated here — it built, linked, and passed the sanitizer and package gates against CXX-Qt 0.9.1 — and 6.12 is the next LTS. CMake ≥ 3.21, C++20. CXX-Qt 0.9.x, Rust 1.83+. +- **CMake shape:** `qt_standard_project_setup()`, `qt_add_executable`, `qt_add_qml_module`. Never hand-rolled `moc` invocations; never `qmake` **as the build system** in a new project. Querying `qmake` is a different act and is permitted: CXX-Qt and some Qt tooling locate Qt that way, and on a distribution with split Qt outputs it is the only discovery path that works (see *Qt discovery on Nix* in the Rust reference). - **C++ gates:** everything in `spacecraft-cpp-guidelines` — `-Wall -Wextra -Wpedantic -Werror`, `-D_GLIBCXX_ASSERTIONS` / `-D_LIBCPP_HARDENING_MODE`, `-fhardened`, sanitizers in Debug. Plus `clazy` (Qt-semantic warnings) and `clang-tidy`, both failing the build. - **QML gates:** the `all_qmllint` target generated by `qt_add_qml_module`, and `qmlformat --verify`. - **Rust gates:** `cargo clippy --all-targets -- -D warnings`, `cargo fmt --check`, `cargo audit`, `cargo test`. diff --git a/spacecraft-qt-guidelines/references/Spacecraft_Qt_Cpp_Guidelines.md b/spacecraft-qt-guidelines/references/Spacecraft_Qt_Cpp_Guidelines.md index 894d128..ab5847b 100644 --- a/spacecraft-qt-guidelines/references/Spacecraft_Qt_Cpp_Guidelines.md +++ b/spacecraft-qt-guidelines/references/Spacecraft_Qt_Cpp_Guidelines.md @@ -278,7 +278,7 @@ endif() target_link_libraries(telemetry-dashboard PRIVATE Qt6::Widgets Qt6::Quick) ``` -Never hand-invoke `moc`, `uic`, or `rcc` — `qt_standard_project_setup()` wires them. Never introduce `qmake` into a new project. +Never hand-invoke `moc`, `uic`, or `rcc` — `qt_standard_project_setup()` wires them. Never make `qmake` the build system of a new project: CMake owns the build graph, and a `.pro` file beside `CMakeLists.txt` is two sources of truth. **Querying** `qmake` is a different act and is not a violation — it is how CXX-Qt and parts of Qt's own tooling locate an installation, and on a distribution with split Qt outputs it is the only discovery path that works (see *Qt discovery on Nix* in `Spacecraft_Qt_Rust_Guidelines.md`). --- @@ -422,6 +422,7 @@ finish-args: | **Custom-painted widget** | Screen reader reports one empty region | Subclass `QAccessibleInterface` and publish children. | | **Hex literals in QSS** | §11.1 violation; theme cannot be swapped | Generate `steelbore.qss` from tokens. | | **LTO on NixOS** | Link failure: LTO plugin not found | §3.2.1 — pair with `-fuse-ld=mold`, or document it disabled. | +| **Split Qt outputs (Nix)** | Qt found, but a module's `.prl` is missing; then `libGLX`/`libOpenGL` fail to link | One combined `qt6.env` plus `libglvnd`; `qmake` queried for discovery only, never as the build system. | --- diff --git a/spacecraft-qt-guidelines/references/Spacecraft_Qt_QML_Guidelines.md b/spacecraft-qt-guidelines/references/Spacecraft_Qt_QML_Guidelines.md index c0d0d7f..207bcd9 100644 --- a/spacecraft-qt-guidelines/references/Spacecraft_Qt_QML_Guidelines.md +++ b/spacecraft-qt-guidelines/references/Spacecraft_Qt_QML_Guidelines.md @@ -177,6 +177,13 @@ Connections { Custom-drawn `Canvas` items must expose their contents as accessible children or a textual summary — a painted chart with no `Accessible` declaration is invisible to a screen reader, exactly as a `GtkDrawingArea` or a custom-painted `QWidget` would be (§18.3). Honour system reduced-motion independently of the §18.1 toggle before enabling any `Animation`. +**Known Qt 6.11 diagnostic — not your bug.** Rebuilding dynamic scene nodes (a `Repeater` +or a model-driven `ListView` whose delegates are recreated) makes Qt 6.11 emit repeated +stale-accessible-path warnings. The tree itself stays correct: Orca exposes and operates it +normally. Do not chase the log, and do not paper over it by pinning `Accessible` properties +onto recycled delegates — that trades a harmless warning for a genuinely wrong tree. The +§18.4 gate is an Orca run against the built application, never a clean console. + --- ## 5. Theming with `steelbore` Tokens @@ -284,6 +291,7 @@ TestCase { | **Deep delegates without `reuseItems`** | Scrolling stutters on long lists | `reuseItems: true`; flatten the delegate. | | **Icon-only `Button`** | Screen reader announces "button" | Set `Accessible.role` and `Accessible.name`. | | **Custom `Canvas` control** | Invisible to assistive technology | Declare accessible children or a textual summary (§18.3). | +| **Stale-accessible-path warnings (Qt 6.11)** | Repeated diagnostics while delegates are rebuilt | Known Qt behaviour with dynamic scene nodes; verify the tree with Orca (§18.4) rather than silencing the log. | | **Hex literals in QML** | §11.1 violation; theme cannot be swapped | Reference the generated `Theme` singleton. | | **`qmllint` warnings ignored** | Type errors surface at run time, in front of a user | Make `all_qmllint` a failing CI gate. | diff --git a/spacecraft-qt-guidelines/references/Spacecraft_Qt_Rust_Guidelines.md b/spacecraft-qt-guidelines/references/Spacecraft_Qt_Rust_Guidelines.md index a59a893..f3a0f0c 100644 --- a/spacecraft-qt-guidelines/references/Spacecraft_Qt_Rust_Guidelines.md +++ b/spacecraft-qt-guidelines/references/Spacecraft_Qt_Rust_Guidelines.md @@ -72,6 +72,50 @@ fn main() { cargo audit ``` +### 2.1 Qt Discovery on Nix — `qmake` Is a Query, Not a Build System + +`cxx-qt-build` locates Qt by **querying `qmake`**, and it assumes what a vendor SDK +provides: QtBase and QtDeclarative under one prefix. Nix does not package Qt that way — +each module is its own immutable output — so the assumption fails and the build stops on a +missing `Qt6Qml.prl`. Combining the outputs then exposes a second split: `libGLX` and +`libOpenGL` come from `libglvnd`, not from any Qt output. + +Both are packaging mismatches, not Qt API breakage. Qt 6.11 with CXX-Qt 0.9.1 builds, +links, and passes the sanitizer and package gates once the environment is assembled. + +**The pattern that works** — one combined environment, used by the devShell and the +package alike, with `qmake` exported for discovery only: + +```nix +qtEnvironment = qt6.env "${packageName}-qt" [ qt6.qtdeclarative ]; + +buildInputs = [ libglvnd qt6.qtbase qtEnvironment ]; + +preBuild = '' + export QMAKE="${qtEnvironment}/bin/qmake" + export QT_VERSION_MAJOR=6 + export RUSTFLAGS="''${RUSTFLAGS-} -C link-arg=-fuse-ld=mold" +''; +``` + +Three things carry the rule: + +- **`qt6.env` is what merges the split outputs.** Listing `qt6.qtbase` and + `qt6.qtdeclarative` as separate `buildInputs` does not give `qmake` one prefix to report. +- **`libglvnd` is not optional** on a Quick/OpenGL build, and its absence surfaces only at + link time, after the `.prl` problem is already solved. +- **`-fuse-ld=mold`** is the §3.2.1 linker requirement, not a Qt detail; it belongs in the + same block. + +`QMAKE` here is a **discovery oracle**. The build system remains `cxx-qt-build` driven by +Cargo: no `.pro` file, no `qmake -o Makefile`, no qmake-generated build graph. That +distinction is the whole of the rule — "never qmake" as an absolute forbids a working +build for no safety gain. + +Reference implementation: `majestic-codex/c1/packaging/default.nix` (derivation) and +`majestic-codex/c1/flake.nix` (devShell). Read those rather than re-deriving the +environment. + --- ## 3. The Bridge: Properties, Signals, Invokables @@ -305,6 +349,7 @@ CI gates: | **Model `Vec` mutated without row signals** | View shows stale rows, then reads out of range and crashes | Wrap mutations in `begin_insert_rows` / `end_insert_rows`. | | **Reaching for a Widgets feature** | CXX-Qt has no binding for it | Check the coverage boundary in §1 — that layer is C++. | | **Qt version mismatch** | Link errors in `cxx-qt-build` | Pin the Qt found by CMake to the 6.8 LTS floor; keep `cxx-qt-lib` features aligned. | +| **Split Qt outputs (Nix)** | `Qt6Qml.prl` not found; once past it, missing `libGLX`/`libOpenGL` | One combined `qt6.env` plus `libglvnd`; export `QMAKE` for query-only discovery (§2.1). | | **Blocking in a `#[qinvokable]`** | UI freezes while the invokable runs | Invokables run on the GUI thread — spawn and queue back. | ---