Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified spacecraft-qt-guidelines.skill
Binary file not shown.
Binary file modified spacecraft-qt-guidelines.zip
Binary file not shown.
4 changes: 2 additions & 2 deletions spacecraft-qt-guidelines/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

---

Expand Down Expand Up @@ -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. |

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +183 to +184

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep accessibility properties on recycled delegates

For a model-driven ListView with interactive delegates, this warning discourages the same Accessible.role and Accessible.name bindings that the example at lines 115–139 and the checklist require. Following it literally leaves recycled controls unnamed or without an explicit role, making the verified tree less accessible. Identify the specific warning-suppression workaround that is unsafe rather than broadly telling readers not to attach Accessible properties.

Useful? React with 👍 / 👎.

§18.4 gate is an Orca run against the built application, never a clean console.

---

## 5. Theming with `steelbore` Tokens
Expand Down Expand Up @@ -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. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +115 to +117

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bundle or replace the referenced Nix implementation

These majestic-codex/c1/... paths do not exist anywhere in this repository, so consumers installing the standalone skill bundle cannot follow the instruction to read them instead of deriving the environment. Include the reference implementation under this skill's references/ directory or replace this with a public, resolvable reference.

AGENTS.md reference: AGENTS.md:L72-L76

Useful? React with 👍 / 👎.


---

## 3. The Bridge: Properties, Signals, Invokables
Expand Down Expand Up @@ -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. |

---
Expand Down
Loading