From 1e575e179bfaa3215c5ed1370d444af0c9407757 Mon Sep 17 00:00:00 2001 From: meh Date: Sun, 13 Sep 2026 09:16:59 +0700 Subject: [PATCH 1/7] fix(qa): reuse released headless browser --- .github/actions/headless-host/action.yml | 87 +++++++++++++++---- .github/workflows/ci.yml | 46 ++++++++++ .github/workflows/release.yml | 68 +++++++++++++++ Cargo.toml | 2 +- README.md | 13 +-- .../frontend/local-ui/bundle/package.json | 2 +- apps/chuzz/frontend/local-ui/package.json | 2 +- apps/chuzz/frontend/package.json | 2 +- docs/distribution.md | 8 ++ scripts/package-headless.sh | 52 +++++++++++ 10 files changed, 256 insertions(+), 26 deletions(-) create mode 100755 scripts/package-headless.sh diff --git a/.github/actions/headless-host/action.yml b/.github/actions/headless-host/action.yml index b6a8ba0..69c6572 100644 --- a/.github/actions/headless-host/action.yml +++ b/.github/actions/headless-host/action.yml @@ -1,12 +1,17 @@ name: Headless browser host description: > - Build chuzz-headless and install ps-qa, so a site's built output can be driven - through a real browser engine with no window. + Download the released chuzz-headless browser and install ps-qa, so a site's + built output can be driven through a real browser engine with no window. inputs: chuzz-ref: - description: The chuzz revision to build the host from. + description: The chuzz revision to build only when no released binary exists. default: master + chuzz-version: + description: > + Released browser version. Empty reads the version from the action's own + Cargo.toml, keeping callers on the binary released with this action. + default: "" token: description: > A token that can read pathscale/chuzz. Optional: chuzz is public, so the @@ -26,20 +31,64 @@ inputs: file can use a field an older driver does not know, and the failure then reads as a broken workflow rather than a driver that is too old. - 0.7.1 uses the shared control protocol and rejects checks that silently - inherit another file's page. Keep the driver on the same protocol line - as the headless host. - default: "^0.7.1" + 0.7.3 adds full-root inventory support and manual-control reconciliation. + Keep the driver on the same protocol line as the headless host. + default: "^0.7.3" outputs: host: - description: Path to the built chuzz-headless binary. - value: ${{ steps.build.outputs.host }} + description: Path to the verified chuzz-headless binary. + value: ${{ steps.acquire.outputs.host }} runs: using: composite steps: - - name: Check out chuzz + - name: Acquire released browser + id: acquire + shell: bash + env: + REQUESTED_VERSION: ${{ inputs.chuzz-version }} + ACTION_ROOT: ${{ github.action_path }}/../../.. + run: | + if [ "$(uname -s)" != "Linux" ] || [ "$(uname -m)" != "x86_64" ]; then + echo "::error::the released chuzz-headless host supports Linux x86_64" + exit 1 + fi + version="$REQUESTED_VERSION" + if [ -z "$version" ]; then + version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' "$ACTION_ROOT/Cargo.toml" | head -1) + fi + case "$version" in + ''|*[!0-9A-Za-z.+-]*) + echo "::error::invalid chuzz version '$version'" + exit 1 + ;; + esac + + asset=chuzz-headless-x86_64-unknown-linux-gnu.tar.gz + release=https://github.com/pathscale/chuzz/releases/download/headless-v$version + directory="$RUNNER_TEMP/chuzz-headless-$version" + mkdir -p "$directory" + + if curl -fsSL --retry 3 --retry-delay 2 "$release/$asset" -o "$directory/$asset" && \ + curl -fsSL --retry 3 --retry-delay 2 "$release/$asset.sha256" -o "$directory/$asset.sha256" && \ + (cd "$directory" && sha256sum -c "$asset.sha256"); then + tar -xzf "$directory/$asset" -C "$directory" + chmod +x "$directory/chuzz-headless" + if [ "$(cat "$directory/VERSION")" != "$version" ]; then + echo "::error::released browser version does not match $version" + exit 1 + fi + echo "released=true" >> "$GITHUB_OUTPUT" + echo "using released chuzz-headless $version" + else + echo "::warning::no verified chuzz-headless $version release; building ${{ inputs.chuzz-ref }} from source" + echo "released=false" >> "$GITHUB_OUTPUT" + fi + echo "host=$directory/chuzz-headless" >> "$GITHUB_OUTPUT" + + - name: Check out chuzz source fallback + if: steps.acquire.outputs.released != 'true' uses: actions/checkout@v4 with: repository: pathscale/chuzz @@ -56,26 +105,30 @@ runs: shell: bash run: | sudo apt-get update - sudo apt-get install -y pkg-config libfontconfig1-dev fonts-dejavu-core + packages=(fontconfig fonts-dejavu-core) + if [ "${{ steps.acquire.outputs.released }}" != "true" ]; then + packages+=(pkg-config libfontconfig1-dev) + fi + sudo apt-get install -y "${packages[@]}" - uses: Swatinem/rust-cache@v2 + if: steps.acquire.outputs.released != 'true' with: workspaces: .qa-host # A failing check must not make the corrective run rebuild a browser # engine from zero. cache-on-failure: true - # The driver links no renderer, so it installs from the registry. The host - # is built here because it is the browser, and the browser is not a crate a - # site can install. + # The driver links no renderer, so it installs from the registry. The + # browser is a versioned release asset built once by Chuzz's release job. - name: Install ps-qa shell: bash env: CARGO_TARGET_DIR: .qa-host/target/qa-tools run: cargo install ps-qa --version "${{ inputs.ps-qa-version }}" - - name: Build the host - id: build + - name: Build source fallback + if: steps.acquire.outputs.released != 'true' shell: bash run: | # `--no-default-features` drops `gui`, and with it `tauri`, `muda -> gtk` @@ -84,4 +137,4 @@ runs: cargo build --release --manifest-path .qa-host/Cargo.toml \ --bin chuzz-headless --no-default-features \ --features capture,javascript,scrollbars,webp,system-fonts - echo "host=$PWD/.qa-host/target/release/chuzz-headless" >> "$GITHUB_OUTPUT" + cp .qa-host/target/release/chuzz-headless "${{ steps.acquire.outputs.host }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33a3e9d..293a30d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,52 @@ jobs: - name: Test run: cargo test -p chuzz-control --all-features + # The fleet's website QA consumes this exact feature set as a prebuilt Linux + # release asset. Build and drive it here once, in the repository that owns + # the browser, before a version can reach the release workflow. + headless: + runs-on: ubicloud-standard-4 + steps: + - uses: actions/checkout@v4 + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Install native font dependencies + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libfontconfig1-dev fonts-dejavu-core + + - name: Cache cargo build + uses: Swatinem/rust-cache@v2 + with: + shared-key: chuzz-linux-headless-ci + cache-targets: true + + - name: Build and package the headless browser + run: scripts/package-headless.sh + + - name: Test the packaged feature set + run: | + cargo test --release -p chuzz \ + --no-default-features \ + --features capture,javascript,scrollbars,webp,system-fonts \ + --test serves_inspection + + - name: Verify the release archive + run: | + cd target/headless-release + sha256sum -c chuzz-headless-x86_64-unknown-linux-gnu.tar.gz.sha256 + tar -tzf chuzz-headless-x86_64-unknown-linux-gnu.tar.gz + + - name: Keep the candidate for review + uses: actions/upload-artifact@v4 + with: + name: chuzz-headless-x86_64-unknown-linux-gnu + path: target/headless-release/chuzz-headless-x86_64-unknown-linux-gnu.tar.gz* + if-no-files-found: error + retention-days: 7 + # The frontend, which is where most changes actually land. Independent of the # Rust jobs, so a TypeScript error does not wait on a cargo build. # diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb6f0cc..7564c6e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,6 +55,7 @@ jobs: outputs: version: ${{ steps.decide.outputs.version }} publish: ${{ steps.decide.outputs.publish }} + headless_publish: ${{ steps.decide.outputs.headless_publish }} steps: - name: Checkout uses: actions/checkout@v4 @@ -101,6 +102,73 @@ jobs: echo "Live is already $LIVE; nothing to do. Bump [workspace.package] version to release." fi + HEADLESS=https://github.com/pathscale/chuzz/releases/download/headless-v$VERSION/chuzz-headless-x86_64-unknown-linux-gnu.tar.gz + if [ "${{ inputs.force }}" = "true" ]; then + echo "headless_publish=true" >> "$GITHUB_OUTPUT" + elif curl -fsSIL --max-time 30 "$HEADLESS" >/dev/null 2>&1 && \ + curl -fsSIL --max-time 30 "$HEADLESS.sha256" >/dev/null 2>&1; then + echo "headless_publish=false" >> "$GITHUB_OUTPUT" + echo "The Linux headless asset for $VERSION is already published." + else + echo "headless_publish=true" >> "$GITHUB_OUTPUT" + echo "The Linux headless asset for $VERSION is missing; publishing it." + fi + + # Build the Linux QA browser once per Chuzz release. Fleet repositories use + # the checksum-verified asset through the composite headless-host action; + # they no longer each compile the same renderer and browser graph. + headless: + needs: check + if: needs.check.outputs.headless_publish == 'true' + runs-on: ubicloud-standard-4 + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Install native font dependencies + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libfontconfig1-dev fonts-dejavu-core + + - name: Cache cargo build + uses: Swatinem/rust-cache@v2 + with: + shared-key: chuzz-linux-headless-release + cache-targets: true + + - name: Build and package the headless browser + run: scripts/package-headless.sh + + - name: Test the packaged feature set + run: | + cargo test --release -p chuzz \ + --no-default-features \ + --features capture,javascript,scrollbars,webp,system-fonts \ + --test serves_inspection + + - name: Publish the versioned headless asset + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ needs.check.outputs.version }} + run: | + tag="headless-v$VERSION" + if ! gh release view "$tag" >/dev/null 2>&1; then + gh release create "$tag" \ + --target "$GITHUB_SHA" \ + --title "Chuzz headless $VERSION" \ + --notes "Linux x86_64 native ps-qa host with capture, JavaScript, scrollbars, WebP, and system-font discovery." \ + --prerelease + fi + gh release upload "$tag" \ + target/headless-release/chuzz-headless-x86_64-unknown-linux-gnu.tar.gz \ + target/headless-release/chuzz-headless-x86_64-unknown-linux-gnu.tar.gz.sha256 \ + --clobber + release: needs: check if: needs.check.outputs.publish == 'true' diff --git a/Cargo.toml b/Cargo.toml index 6a2a012..ac615c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["apps/chuzz", "crates/chuzz-control"] resolver = "3" [workspace.package] -version = "0.1.38" +version = "0.1.39" edition = "2024" rust-version = "1.91" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 935bcd7..40e6e73 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Events are not wired up yet: the page renders and does not respond. ## Rendering without a window For interactive website QA, build the headless host with fonts and use ps-qa -0.7.1 or newer: +0.7.3 or newer: ```sh cargo build --release --bin chuzz-headless --no-default-features \ @@ -106,10 +106,13 @@ ps-qa --app ../worktables.dev/tests/ps-qa/ps-qa.ron qa-hosted \ --checks ../worktables.dev/tests/ps-qa/checks ``` -Linux needs `pkg-config`, `libfontconfig1-dev`, and a font catalogue such as -`fonts-dejavu-core`; the shared headless-host CI action installs these. No desktop -server is needed. The `system-fonts` feature is optional for embedders supplying -their own fonts, but rendered website checks need real glyphs. +Linux source builds need `pkg-config`, `libfontconfig1-dev`, and a font catalogue +such as `fonts-dejavu-core`. The shared headless-host action normally downloads +the versioned Linux x86_64 release asset, verifies its published SHA-256, and +installs only the runtime font catalogue. If that asset is unavailable, the +action builds the same feature set from source. No desktop server is needed. The +`system-fonts` feature is optional for embedders supplying their own fonts, but +rendered website checks need real glyphs. The host dispatches input through the shared `DocumentControl` implementation, including pointer gestures, key-down/up, and scrolling. Once an action has been diff --git a/apps/chuzz/frontend/local-ui/bundle/package.json b/apps/chuzz/frontend/local-ui/bundle/package.json index b512770..db3b913 100644 --- a/apps/chuzz/frontend/local-ui/bundle/package.json +++ b/apps/chuzz/frontend/local-ui/bundle/package.json @@ -21,7 +21,7 @@ "**/*" ], "peerDependencies": { - "@pathscale/ui": "^3.1.0", + "@pathscale/ui": "^3.2.3", "solid-js": ">=2.0.0-rc.0", "solid-layouts": "^0.2.1" }, diff --git a/apps/chuzz/frontend/local-ui/package.json b/apps/chuzz/frontend/local-ui/package.json index 86cf80b..5b6cf68 100644 --- a/apps/chuzz/frontend/local-ui/package.json +++ b/apps/chuzz/frontend/local-ui/package.json @@ -9,7 +9,7 @@ "**/*.css" ], "peerDependencies": { - "@pathscale/ui": "^3.1.0", + "@pathscale/ui": "^3.2.3", "solid-js": ">=2.0.0-rc.0", "solid-layouts": "^0.2.1" } diff --git a/apps/chuzz/frontend/package.json b/apps/chuzz/frontend/package.json index 898a972..88df410 100644 --- a/apps/chuzz/frontend/package.json +++ b/apps/chuzz/frontend/package.json @@ -29,7 +29,7 @@ "dependencies": { "@chuzz/ui": "workspace:*", "@iconify/tailwind4": "^1.0.6", - "@pathscale/ui": "^3.1.0", + "@pathscale/ui": "^3.2.3", "@solidjs/web": "2.0.0-rc.4", "@tauri-apps/api": "^2.1.1", "clsx": "^2.1.1", diff --git a/docs/distribution.md b/docs/distribution.md index 6f2ecec..cf3f2ec 100644 --- a/docs/distribution.md +++ b/docs/distribution.md @@ -89,6 +89,14 @@ it into the bundle's `Info.plist` at build time, and the workflow refuses to publish if the two disagree, so the cask can never advertise a version the app does not report. +The same release builds `chuzz-headless` on Linux x86_64 with capture, +JavaScript, scrollbars, WebP, and system-font discovery enabled. It publishes a +versioned GitHub prerelease asset and SHA-256 under `headless-v`. Fleet +sites consume that verified binary through the shared headless-host action, so +the browser is compiled once in its own repository rather than once per site. +The action falls back to the identical source build if the release asset is not +yet available. + [`release.yml`](../.github/workflows/release.yml) decides whether to publish by comparing that version against the one in the **live** `latest.json`, not against git history. That makes it idempotent: a release that failed halfway is retried diff --git a/scripts/package-headless.sh b/scripts/package-headless.sh new file mode 100755 index 0000000..1f5c393 --- /dev/null +++ b/scripts/package-headless.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# Build the exact Linux browser used by ps-qa and package it as a public, +# checksum-addressed release asset. Fleet sites download this once-built binary +# through the headless-host action instead of recompiling the browser in every +# repository. +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +output="${1:-$root/target/headless-release}" +asset="chuzz-headless-x86_64-unknown-linux-gnu.tar.gz" + +if [[ "$(uname -s)" != "Linux" || "$(uname -m)" != "x86_64" ]]; then + echo "package-headless.sh requires Linux x86_64" >&2 + exit 2 +fi + +version=$(cargo metadata --no-deps --format-version 1 --manifest-path "$root/Cargo.toml" \ + | sed -n 's/.*"name":"chuzz","version":"\([^"]*\)".*/\1/p') +if [[ -z "$version" ]]; then + echo "could not read chuzz's workspace version" >&2 + exit 1 +fi + +cargo build --release --manifest-path "$root/Cargo.toml" \ + --bin chuzz-headless --no-default-features \ + --features capture,javascript,scrollbars,webp,system-fonts + +if ldd "$root/target/release/chuzz-headless" | grep -q 'not found'; then + echo "chuzz-headless has unresolved runtime libraries" >&2 + ldd "$root/target/release/chuzz-headless" >&2 + exit 1 +fi + +stage=$(mktemp -d) +cleanup() { + rm -rf "$stage" +} +trap cleanup EXIT INT TERM + +mkdir -p "$output" +cp "$root/target/release/chuzz-headless" "$stage/chuzz-headless" +printf '%s\n' "$version" > "$stage/VERSION" +cp "$root/LICENSE-APACHE" "$root/LICENSE-MIT" "$stage/" + +COPYFILE_DISABLE=1 tar -czf "$output/$asset" -C "$stage" \ + chuzz-headless VERSION LICENSE-APACHE LICENSE-MIT +( + cd "$output" + sha256sum "$asset" > "$asset.sha256" +) + +printf '%s\n' "$output/$asset" From 86ef07165d250b076930733c5cddd3061589b157 Mon Sep 17 00:00:00 2001 From: meh Date: Sun, 13 Sep 2026 11:13:44 +0700 Subject: [PATCH 2/7] feat(browser): persist profiles and gate native UI --- .github/workflows/ci.yml | 31 +- .github/workflows/release.yml | 19 + .gitignore | 7 +- AGENTS.md | 21 + Cargo.toml | 7 +- apps/chuzz/Cargo.toml | 11 +- .../panel-handle/PanelHandle.layout.tsx | 2 + .../settings-dialog/SettingsDialog.layout.tsx | 11 +- .../surface-swatch/SurfaceSwatch.layout.tsx | 15 +- .../surface-swatch/SurfaceSwatch.recipe.ts | 3 + .../surface-wheel/SurfaceWheel.layout.tsx | 1 + .../components/tab-list/TabList.layout.tsx | 11 +- .../src/components/tab/Tab.layout.tsx | 37 +- .../local-ui/src/components/tab/Tab.recipe.ts | 6 + apps/chuzz/frontend/src/App.tsx | 27 +- apps/chuzz/frontend/src/api/client.ts | 13 +- apps/chuzz/frontend/src/api/mock.ts | 27 +- apps/chuzz/frontend/src/api/tauri.ts | 14 +- .../settings/BrowserIdentitySection.tsx | 46 + .../features/settings/DiagnosticsSection.tsx | 7 + .../src/features/settings/SettingsPanel.tsx | 2 + .../src/features/settings/ThemePicker.tsx | 10 + .../src/features/tabs/BrowserHeader.tsx | 35 +- .../frontend/src/features/toolbar/Toolbar.tsx | 3 +- apps/chuzz/frontend/src/index.tsx | 1 + apps/chuzz/frontend/src/stores/browser.tsx | 22 +- apps/chuzz/frontend/src/stores/i18n.ts | 8 + apps/chuzz/frontend/src/stores/user-agent.ts | 20 + apps/chuzz/frontend/src/types/index.ts | 9 + apps/chuzz/src/browser.rs | 249 ++++- apps/chuzz/src/capture.rs | 29 +- apps/chuzz/src/cookie_store.rs | 910 ++++++++++++++++++ apps/chuzz/src/document_loader.rs | 865 ++++++++++++++++- apps/chuzz/src/identity.rs | 36 +- apps/chuzz/src/lib.rs | 1 + apps/chuzz/src/net_bridge.rs | 84 +- apps/chuzz/src/script_fetch.rs | 5 +- apps/chuzz/src/serve.rs | 21 +- apps/chuzz/src/tauri_main.rs | 15 +- apps/chuzz/tests/follows_a_link.rs | 22 +- .../tests/js_event_property_semantics.rs | 41 + apps/chuzz/tests/serves_inspection.rs | 30 +- crates/chuzz-qa-fixture/Cargo.toml | 14 + crates/chuzz-qa-fixture/src/main.rs | 107 ++ ps-qa.ron | 28 + scripts/qa-visible-app.sh | 128 +++ tests/ps-qa/01-chrome.ron | 108 +++ tests/ps-qa/02-inspector.ron | 43 + tests/ps-qa/03-theme.ron | 97 ++ tests/ps-qa/04-identity.ron | 23 + tests/ps-qa/05-diagnostics.ron | 33 + 51 files changed, 3194 insertions(+), 121 deletions(-) create mode 100644 apps/chuzz/frontend/src/features/settings/BrowserIdentitySection.tsx create mode 100644 apps/chuzz/frontend/src/stores/user-agent.ts create mode 100644 apps/chuzz/src/cookie_store.rs create mode 100644 apps/chuzz/tests/js_event_property_semantics.rs create mode 100644 crates/chuzz-qa-fixture/Cargo.toml create mode 100644 crates/chuzz-qa-fixture/src/main.rs create mode 100644 ps-qa.ron create mode 100755 scripts/qa-visible-app.sh create mode 100644 tests/ps-qa/01-chrome.ron create mode 100644 tests/ps-qa/02-inspector.ron create mode 100644 tests/ps-qa/03-theme.ron create mode 100644 tests/ps-qa/04-identity.ron create mode 100644 tests/ps-qa/05-diagnostics.ron diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 293a30d..348def3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -167,12 +167,9 @@ jobs: - name: Test run: bun run test:run - # The app itself, on the platform it targets. This is the job that proves a - # change compiles against the pinned engine, which no Linux runner can do. - # - # `build-app.sh` is deliberately not used: bundling, signing and stamping are - # the release job's business. A PR only needs to know the code compiles and - # its tests pass. + # The app itself, on the platform it targets. The visible gate drives the + # same signed bundle a release would publish, including browser chrome, + # settings, theme controls, identity switching and the redraw stress page. app: runs-on: namespace-profile-agency-tahoe steps: @@ -205,3 +202,25 @@ jobs: - name: Test run: cargo test --workspace + + - name: Install the native QA harness + run: cargo install ps-qa --version '^0.7.3' + + - name: Build the review bundle + run: | + cargo build --release -p chuzz-qa-fixture + apps/chuzz/build-app.sh release + + - name: Drive the visible application + env: + QA_ARTIFACT_DIR: ${{ runner.temp }}/chuzz-visible-qa + run: scripts/qa-visible-app.sh + + - name: Keep visible QA evidence + if: always() + uses: actions/upload-artifact@v4 + with: + name: chuzz-visible-qa + path: ${{ runner.temp }}/chuzz-visible-qa + if-no-files-found: warn + retention-days: 7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7564c6e..fda56df 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -239,6 +239,9 @@ jobs: with: targets: aarch64-apple-darwin + - name: Install the native QA harness + run: cargo install ps-qa --version '^0.7.3' + - name: Cache cargo build uses: Swatinem/rust-cache@v2 with: @@ -275,6 +278,22 @@ jobs: exit 1 fi + - name: Drive the exact release bundle + env: + QA_ARTIFACT_DIR: ${{ runner.temp }}/chuzz-visible-qa + run: | + cargo build --release -p chuzz-qa-fixture + scripts/qa-visible-app.sh "$BUNDLE_DIR/Chuzz.app" + + - name: Keep visible QA evidence + if: always() + uses: actions/upload-artifact@v4 + with: + name: chuzz-visible-qa-release-${{ needs.check.outputs.version }} + path: ${{ runner.temp }}/chuzz-visible-qa + if-no-files-found: warn + retention-days: 14 + # Tauri produces this tarball as a side effect of its bundler. Chuzz has no # Tauri, so it is packed here. COPYFILE_DISABLE stops bsdtar writing an # AppleDouble `._` sidecar beside every file carrying an extended diff --git a/.gitignore b/.gitignore index 039ce90..98ae50d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,6 @@ node_modules/ apps/chuzz/dist/ -# Handover notes are session state, tracked locally and never committed. -docs/HANDOVER-*.md - # Local-only renderer patch, see the file itself. .cargo/config.toml .cargo/config.toml.disabled @@ -22,6 +19,10 @@ apps/chuzz/gen/ # exist on one machine. See scripts/local-engine.sh. .cargo/local-engine.toml +# Handover and session notes are working state, not repository content. +HANDOVER*.md +docs/HANDOVER*.md + # Lockfiles are not committed here. Every dependency is a caret range on a # published version, so a build resolves the newest thing that satisfies it and # a broken upstream release fails the build that introduced it. A committed lock diff --git a/AGENTS.md b/AGENTS.md index 672ea10..4d06f63 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,6 +56,27 @@ - Work on a branch and ship through a pull request. Do not commit to `main`. - Run `cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, and `cargo test --workspace --all-features` before delivery. +## Invariants (do not break these) + +- **No Python.** Not a script, not `python3 -c`, not a heredoc. Do not swap it + for another ad hoc parser or assume `jq` is present: it does not ship with + macOS. Ask the tool that owns the answer for structured output. A fixed-shape + field can use one `sed -nE` line; logic that needs real parsing belongs in + this repository's Rust code, where it can be tested. + +## Handover documents are never committed + +Never commit a handover, status or session-summary document to this repository. +They are working notes for the owner and belong in the conversation. If one is +tracked, untrack it rather than editing it. + +## Merged is not fixed + +Only the owner closes a bug. A merged PR means the change shipped. Call it +finished only after the owner confirms it or after the exact reported path has +been driven on a running build. When that cannot be verified here, state what +remains unverified. + ## Git workflow - **Always specify the branch when pushing**: `git push origin branch-name` diff --git a/Cargo.toml b/Cargo.toml index ac615c7..8d0d856 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["apps/chuzz", "crates/chuzz-control"] +members = ["apps/chuzz", "crates/chuzz-control", "crates/chuzz-qa-fixture"] resolver = "3" [workspace.package] @@ -110,9 +110,14 @@ rustls = { version = "0.23", default-features = false, features = [ "aws-lc-rs", "std", ] } +cursor-icon = "^1" +cookie_store = "^0.22.1" +psl = "^2" futures-util = { version = "0.3", default-features = false, features = ["sink"] } +nagoya = "^0.1.2" tokio = "1" url = "2.5" +worktable = "^1.9.0-beta1" tauri = { version = "^2.11.5", default-features = false } # `diagnostics`, always, rather than behind a cargo feature. # diff --git a/apps/chuzz/Cargo.toml b/apps/chuzz/Cargo.toml index 1865edb..9f1ed17 100644 --- a/apps/chuzz/Cargo.toml +++ b/apps/chuzz/Cargo.toml @@ -158,16 +158,24 @@ dioxus-native = { workspace = true, optional = true, features = [ "prelude", "svg", ] } -tokio = { workspace = true, features = ["macros", "rt", "sync", "time"] } +nagoya.workspace = true +# Tokio remains the I/O boundary for Tauri, HTTP, TCP and WebSocket. Portable +# timers use Nagoya, so this list names only the runtime/synchronisation surface +# those concrete I/O types require. +tokio = { workspace = true, features = ["macros", "rt", "sync"] } url.workspace = true serde.workspace = true serde_json.workspace = true tauri = { workspace = true, optional = true } rustls.workspace = true +cursor-icon.workspace = true +cookie_store.workspace = true +psl.workspace = true blitz-control-protocol = { workspace = true, features = ["engine", "server", "capture"] } tauri-runtime-blitz = { workspace = true, optional = true } tokio-tungstenite.workspace = true futures-util.workspace = true +worktable.workspace = true [dev-dependencies] # `flavor = "multi_thread"` in the script-fetch test: the blocking fetch takes @@ -182,7 +190,6 @@ tokio = { workspace = true, features = [ "rt", "rt-multi-thread", "sync", - "time", ] } # Assembles `fixtures/panel.wat` into a module in-process, so the capture test # owns its guest instead of borrowing blitz-wasm's demo, and so it needs no diff --git a/apps/chuzz/frontend/local-ui/src/components/panel-handle/PanelHandle.layout.tsx b/apps/chuzz/frontend/local-ui/src/components/panel-handle/PanelHandle.layout.tsx index 5610e5c..f1391e6 100644 --- a/apps/chuzz/frontend/local-ui/src/components/panel-handle/PanelHandle.layout.tsx +++ b/apps/chuzz/frontend/local-ui/src/components/panel-handle/PanelHandle.layout.tsx @@ -3,6 +3,7 @@ import type { Layout } from "solid-layouts"; import { panelHandle } from "./PanelHandle.recipe"; export type PanelHandleProps = { + id: string; title: string; collapsed?: boolean; onClick: JSX.EventHandlerUnion; @@ -18,6 +19,7 @@ export type PanelHandleProps = { */ const PanelHandle: Layout = () => ( + + + + + ); +} diff --git a/apps/chuzz/frontend/src/features/settings/DiagnosticsSection.tsx b/apps/chuzz/frontend/src/features/settings/DiagnosticsSection.tsx index f4179b2..e9d1a64 100644 --- a/apps/chuzz/frontend/src/features/settings/DiagnosticsSection.tsx +++ b/apps/chuzz/frontend/src/features/settings/DiagnosticsSection.tsx @@ -33,6 +33,7 @@ export function DiagnosticsSection(): JSX.Element {