Skip to content

feat(ci): Sprint 1 smoke E2E + tauri-pilot wiring (T-134)#66

Merged
mpiton merged 7 commits into
mainfrom
feat/t134-smoke-e2e
May 11, 2026
Merged

feat(ci): Sprint 1 smoke E2E + tauri-pilot wiring (T-134)#66
mpiton merged 7 commits into
mainfrom
feat/t134-smoke-e2e

Conversation

@mpiton
Copy link
Copy Markdown
Owner

@mpiton mpiton commented May 11, 2026

Closes #48

Summary

Sprint 1 acceptance gate (T-134). Adds a runnable smoke E2E that boots pnpm tauri dev, drives the app through tauri-pilot, and asserts the five Sprint 1 ACs. Wires tauri-plugin-pilot behind an opt-in pilot cargo feature so release bundles never ship the pilot socket surface.

Why

T-134 is the Sprint 1 livrable (ARCHI §21). It is the first time the full chain App.tsx (T-130)tasks_list IPC (T-120) ↔ libsql (T-116) ↔ bootstrap logs (T-105) is exercised end-to-end against a real WebView. The smoke surfaced two regressions that would have shipped to Sprint 1 sign-off otherwise (see "Bugs surfaced + fixed" below).

Changes

  • scripts/smoke-e2e.sh — bash runner. Boots pnpm tauri dev --features pilot, polls tauri-pilot ping (default 180 s / 300 s CI), then asserts:
    1. Sidebar <aside> mounted in the DOM snapshot (locale-agnostic — accepts EN Main navigation, FR Navigation principale, or the structural data-testid="usage-indicator")
    2. tauri-pilot ipc tasks_list --args '{}' returns []
    3. <repo>/.claude/forgent/tasks.db exists
    4. ~/.forgent/logs/forgent.log.YYYY-MM-DD contains Forgent ready
    5. target/smoke-screenshot.png non-empty
  • .github/workflows/smoke-e2e.ymlworkflow_dispatch-only Linux job. Installs tauri-pilot-cli + xvfb, runs the smoke under xvfb-run, uploads the screenshot artifact on success and target/smoke-logs/ on failure. Sprint 1 sign-off accepts the manual local run; full matrix lands in Sprint 10.
  • src-tauri/Cargo.tomltauri-plugin-pilot = "0.5.1" (crates.io, not git) added as --optional. New pilot feature toggles the plugin + capability emission.
  • src-tauri/build.rs — emits capabilities/pilot.json (granting pilot:default on the main window) iff cfg!(feature = "pilot"). Idempotent (fs::read_to_string + content compare) so it does not loop the Tauri dev watcher.
  • src-tauri/src/lib.rs — feature-gated .plugin(tauri_plugin_pilot::init()) wiring + removal of tauri_plugin_log (see fix below).
  • src-tauri/.gitignore — ignores the emitted capabilities/pilot.json.
  • CHANGELOG.md[Unreleased] Added entry with full rationale.

Bugs surfaced + fixed

  1. tracing subscriber double-init panic (T-120 regression). tauri_plugin_log::Builder::default().build() installed a global tracing subscriber at plugin build time, then bootstrap() observability::init(&cfg) panicked with "attempted to set a logger after the logging system was already initialized". The plugin was redundant (our own layer covers JSON file logging + Sentry + redaction; no frontend import of @tauri-apps/plugin-log) — removed via cargo remove tauri-plugin-log.
  2. DB path lands under src-tauri/ instead of repo root. AppConfig::load() uses current_dir(), but tauri-cli cds into src-tauri/ before invoking cargo run. The smoke script now exports FORGENT_PROJECT_ROOT="$REPO_ROOT" before launching the dev server so AC4 (DB at <project>/.claude/forgent/tasks.db) passes.

Acceptance criteria

  • pnpm tauri dev opens a window
  • Sidebar visible
  • tauri-pilot ipc tasks_list --args '{}' returns []
  • DB file <project>/.claude/forgent/tasks.db exists
  • Logs file ~/.forgent/logs/forgent.log.YYYY-MM-DD exists with Forgent ready line
  • Screenshot captured (see comment below)

Test plan

./scripts/smoke-e2e.sh
# → [smoke] PASS — Sprint 1 acceptance gate met

Verified locally on 2026-05-11:

  • cargo clippy --workspace -- -D warnings — clean (5 pre-existing ts-rs transparent parse warnings, none new)
  • cargo test --workspace --lib — 169/169 pass
  • cargo deny check — advisories ok, bans ok, licenses ok, sources ok
  • pnpm exec tsc -b — clean
  • pnpm exec oxlint --max-warnings=0 . — 0 warnings / 0 errors across 39 files
  • pnpm exec vitest run — 106/106 pass

Screenshot

smoke-screenshot

Summary by CodeRabbit

  • Tests

    • Added a manual smoke end-to-end workflow that runs headless, verifies core UI and IPC behavior, checks DB/log freshness, captures a screenshot, and uploads logs on failure.
  • Chores

    • Introduced an optional "pilot" feature for test wiring, removed a conflicting logging plugin, and made capability metadata generation conditional to avoid build churn.
  • Bug Fixes

    • Filtered out noisy pilot-related debug IPC errors from telemetry.
  • Documentation

    • Updated changelog with the Sprint 1 smoke E2E acceptance gate and CI behavior.

Review Change Stack

mpiton added 2 commits May 11, 2026 11:08
Adds the Sprint 1 acceptance gate as a runnable script:
- `scripts/smoke-e2e.sh` boots `pnpm tauri dev --features pilot`,
  waits on `tauri-pilot ping`, then asserts Sidebar mounted,
  `tasks_list` IPC returns `[]`, DB file at `<repo>/.claude/forgent/tasks.db`,
  log file with `Forgent ready` line, and a PNG screenshot.
- `.github/workflows/smoke-e2e.yml` runs the same flow under xvfb on
  `workflow_dispatch` (Sprint 1 accepts manual sign-off; full matrix
  lands in Sprint 10 alongside the disabled `e2e` job in ci.yml).

Wires `tauri-plugin-pilot` behind an opt-in `pilot` cargo feature so
release bundles never ship the pilot socket. `build.rs` emits the
`pilot:default` capability file only when the feature is active, with
an idempotent write to avoid looping the Tauri dev watcher.

Drops `tauri-plugin-log` (redundant with our own tracing layer and
collided with `infrastructure::observability::init` — caused the
"attempted to set a logger" panic exposed by the smoke runner).
The crate is published on crates.io (0.5.1) — cleaner than pulling
from the `mpiton/tauri-pilot` repo and keeps `deny.toml`'s
`unknown-git = "deny"` gate clean (no `allow-git` exception needed).
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements the Sprint 1 smoke E2E acceptance gate: adds an optional Cargo feature pilot and optional tauri-plugin-pilot, conditionally generates capabilities/pilot.json in build.rs, removes tauri-plugin-log, wires the pilot plugin at runtime, adds a robust Bash smoke script that validates UI, IPC, DB, and logs, and adds a manual Ubuntu GitHub Actions workflow to run it headless and upload artifacts.

Changes

Smoke E2E Testing Infrastructure

Layer / File(s) Summary
CI workflow header & env
.github/workflows/smoke-e2e.yml
Workflow dispatch, permissions, env vars and timeout for the Smoke E2E job.
CI workflow toolchain & caches
.github/workflows/smoke-e2e.yml
Install webkit + xvfb, pin Rust 1.95.0 with cache, setup Node 24/pnpm with cache, install tauri-pilot-cli, run scripts/smoke-e2e.sh under xvfb-run, upload screenshot always and logs on failure.
Changelog
CHANGELOG.md
Document Sprint 1 smoke E2E acceptance gate and required backend/CI changes.
Cargo Features & Dependencies
src-tauri/Cargo.toml
Removed tauri-plugin-log; added optional tauri-plugin-pilot = { version = "0.5.1", optional = true } and pilot feature plus alias feature.
Build script & gitignore
src-tauri/.gitignore, src-tauri/build.rs
Ignore generated /capabilities/pilot.json; build.rs conditionally writes or removes capabilities/pilot.json when pilot feature toggles, using content-diff and emitting Cargo rerun directives.
Runtime plugin wiring
src-tauri/src/lib.rs
Omit tauri_plugin_log from Builder; conditionally register tauri_plugin_pilot::init() when compiled with pilot feature; rely on bootstrap observability init.
Sentry init filter
src/shared/observability/sentry.ts
Add ignoreErrors: [/pilot\./] to Sentry.init to filter pilot.* debug IPC rejection noise.
Smoke Test Script — header & init
scripts/smoke-e2e.sh
Script header, strict Bash, artifact/log-path setup, timeouts, and deterministic TAURI_PILOT_SOCKET naming.
Smoke Test Script — cleanup & helpers
scripts/smoke-e2e.sh
cleanup() trap that kills dev server, reaps children; require() and fail() helpers for preconditions and failure reporting.
Smoke Test Script — start & readiness
scripts/smoke-e2e.sh
Start pnpm tauri dev --features pilot, background process with dev log, poll tauri-pilot ping until ready or timeout.
Smoke Test Script — DOM snapshot
scripts/smoke-e2e.sh
Wait for aside[aria-label], snapshot DOM, validate sidebar landmark (locale text or usage-indicator), write snapshot on failure.
Smoke Test Script — IPC/DB/log checks
scripts/smoke-e2e.sh
Call tauri-pilot ipc tasks_list --args '{}', accept multiple empty-array envelope shapes, verify .claude/forgent/tasks.db exists, and check per-run forgent.log contains "Forgent ready".
Smoke Test Script — screenshot & summary
scripts/smoke-e2e.sh
Capture screenshot via tauri-pilot screenshot, assert non-empty PNG, and print PASS summary.

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant Runner
  participant xvfb
  participant pnpm
  participant TauriDev
  participant tauriPilot
  participant Filesystem
  GitHubActions->>Runner: workflow_dispatch
  Runner->>Runner: apt-get (webkit, xvfb), setup Rust 1.95, Node 24, pnpm
  Runner->>xvfb: xvfb-run start
  xvfb->>pnpm: run `pnpm tauri dev --features pilot`
  pnpm->>TauriDev: launch Tauri app
  tauriPilot->>TauriDev: `tauri-pilot ping` / ipc `tasks_list`
  TauriDev->>Filesystem: create `.claude/forgent/tasks.db` and write logs
  tauriPilot->>Filesystem: capture `target/smoke-screenshot.png`
  Runner->>GitHubActions: upload artifacts (screenshot always, logs on failure)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • mpiton/forgent#55: Modifies src-tauri/src/lib.rs::run() plugin wiring; related to the runtime plugin changes in this PR.
  • mpiton/forgent#60: Related — both PRs change src/shared/observability/sentry.ts and Sentry initialization.
  • mpiton/forgent#2: Early Tauri scaffold/Cargo changes affecting the same backend files.

"I hopped through build.rs and CI light,
Pinned toolchains, plugins tucked up tight;
I pinged the pilot, took a tiny snap—
Sprint One cleared with a rabbit clap!" 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely and accurately summarizes the main change: adding Sprint 1 smoke E2E tests and tauri-pilot integration with the related issue reference (T-134).
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #48 (T-134): smoke test script with five acceptance criteria assertions, GitHub Actions workflow, tauri-plugin-pilot feature gating, capability generation, plugin removal for conflict avoidance, and screenshot evidence.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the T-134 smoke E2E acceptance gate. The removal of tauri-plugin-log is necessary scaffolding to prevent initialization conflicts, and Sentry filtering is a direct support measure for the pilot plugin integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/t134-smoke-e2e

Comment @coderabbitai help to get the list of available commands and usage tips.

@mpiton
Copy link
Copy Markdown
Owner Author

mpiton commented May 11, 2026

Smoke E2E — local validation result

Ran ./scripts/smoke-e2e.sh on Linux 6.17 / X11 / WebKitGTK 4.1 on 2026-05-11:

[smoke] starting pnpm tauri dev --features pilot
[smoke] app reachable via tauri-pilot
[smoke] waiting for Sidebar to mount
[smoke] checking Sidebar landmark in DOM snapshot
[smoke] Sidebar landmark present
[smoke] invoking tasks_list IPC
[smoke] tasks_list returned []
[smoke] checking DB file at <repo>/.claude/forgent/tasks.db
[smoke] DB file present
[smoke] checking log file ~/.forgent/logs/forgent.log.YYYY-MM-DD for 'Forgent ready'
[smoke] 'Forgent ready' log line present
[smoke] capturing screenshot → <repo>/target/smoke-screenshot.png
[smoke] screenshot saved (31763 bytes)

[smoke] PASS — Sprint 1 acceptance gate met

Screenshot (target/smoke-screenshot.png, 31 763 bytes) — please attach manually via the web UI; the artifact contains the Forgent main window with the Sidebar on the left and the "Aucune tâche pour l'instant" (FR locale fallback) empty state centered.

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented May 11, 2026

Merging this PR will not alter performance

✅ 7 untouched benchmarks


Comparing feat/t134-smoke-e2e (857c80c) with main (b04a4b8)

Open in CodSpeed

Smoke output is intended to land in PR descriptions and CI logs.
Previous runs printed `/home/<username>/projets/forgent/...` which
leaks the developer's local layout and username when pasted publicly.

Introduces `*_DISPLAY` variables (`<repo>/...`, `~/...`) used in
every `echo` and `fail` message. Filesystem reads still use the real
absolute paths internally — only the rendered output changes.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/smoke-e2e.yml:
- Around line 61-62: The workflow step that runs `cargo install tauri-pilot-cli`
should pin the package to the 0.5.x line and use a locked install to ensure
compatibility with the smoke-e2e script's use of `tauri-pilot ipc --args`;
update the `tauri-pilot-cli` install invocation to specify a 0.5.x version
(e.g., =0.5.1 or ^0.5) and add `--locked` so the installed CLI matches
`tauri-plugin-pilot = "0.5.1"` and preserves the `--args` behavior relied on by
`scripts/smoke-e2e.sh`.

In `@scripts/smoke-e2e.sh`:
- Around line 53-62: The pkill -f fallbacks are too broad; start the dev server
(the tauri/vite/cargo run invocation) in a new process group/session (e.g., via
setsid or start-stop-daemon) and capture its leader PID (DEV_PGID or DEV_PID),
then replace the global pkill -f lines (the pkill -TERM -f
"target/debug/forgent" and pkill -TERM -f "node .*vite" and their KILL
fallbacks) with targeted group kills like kill -TERM -<DEV_PGID> and kill -KILL
-<DEV_PGID> so only the smoke run’s process tree is torn down; make the same
change for the corresponding cleanup at the later occurrence (lines 94-95).
- Around line 28-30: Before starting the smoke run, record the current state and
write a unique run marker: generate a RUN_ID (e.g., uuid or timestamp), append
"SMOKE_RUN_START:$RUN_ID" to LOG_FILE, and capture DB_PATH and LOG_FILE initial
metadata (size or mtime). After the run completes, assert the log contains the
marker and that DB_PATH changed (compare mtime/size) to ensure the invocation
caused new activity; replace the existing "exists/contains" checks with these
checks referencing DB_PATH and LOG_FILE and keep READY_TIMEOUT logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 425f106a-ff73-4bbe-b1a6-0dafc6c9f1c9

📥 Commits

Reviewing files that changed from the base of the PR and between b04a4b8 and 2250acd.

⛔ Files ignored due to path filters (1)
  • src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .github/workflows/smoke-e2e.yml
  • CHANGELOG.md
  • scripts/smoke-e2e.sh
  • src-tauri/.gitignore
  • src-tauri/Cargo.toml
  • src-tauri/build.rs
  • src-tauri/src/lib.rs

Comment thread .github/workflows/smoke-e2e.yml Outdated
Comment thread scripts/smoke-e2e.sh
Comment thread scripts/smoke-e2e.sh Outdated
`tauri-plugin-pilot` is debug-only (`pilot` cargo feature, T-134) and
its JS bridge can emit unhandled `pilot.__callback not allowed`
rejections during dev when build.rs rewrites the capability file
between rebuilds. Those errors will never reach a release build, so
filter them at the Sentry SDK boundary via `ignoreErrors: [/pilot\./]`.

Fixes RUST-2.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 8 files

Three findings from CodeRabbit:
- scope cleanup to the dev server's process group via `setsid` so
  `kill -- -$DEV_PID` does not reap unrelated user processes that
  happen to match `target/debug/forgent` or `node …vite`
- assert DB and log freshness against a start-of-run marker
  (mtime + byte offset) so leftovers from a previous green run
  do not mask a silent bootstrap regression
- pin `cargo install tauri-pilot-cli` to `=0.5.1 --locked` so the
  smoke gate cannot break when the CLI ships an incompatible 0.6

Smoke re-validated locally — PASS with new freshness markers.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
scripts/smoke-e2e.sh (1)

156-159: 💤 Low value

Consider -E flag for grep portability.

The \| alternation syntax is GNU grep-specific; BSD grep (macOS) requires -E with |. Since CI is Linux-only this isn't blocking, but local dev on macOS would fail.

🔧 Portable grep pattern
-if ! grep -q "Main navigation\|Navigation principale\|usage-indicator" <<<"$snapshot"; then
+if ! grep -Eq "Main navigation|Navigation principale|usage-indicator" <<<"$snapshot"; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/smoke-e2e.sh` around lines 156 - 159, The grep alternation uses
GNU-only '\|' syntax; update the grep invocation used in the conditional (the
line starting with if ! grep -q "Main navigation\|Navigation
principale\|usage-indicator" <<<"$snapshot"; then) to a portable form by
enabling extended regex (e.g., add -E: grep -q -E "Main navigation|Navigation
principale|usage-indicator" <<<"$snapshot") so the alternation works on
BSD/macOS as well while keeping the -q behavior and existing redirect of
<<<"$snapshot".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@scripts/smoke-e2e.sh`:
- Around line 156-159: The grep alternation uses GNU-only '\|' syntax; update
the grep invocation used in the conditional (the line starting with if ! grep -q
"Main navigation\|Navigation principale\|usage-indicator" <<<"$snapshot"; then)
to a portable form by enabling extended regex (e.g., add -E: grep -q -E "Main
navigation|Navigation principale|usage-indicator" <<<"$snapshot") so the
alternation works on BSD/macOS as well while keeping the -q behavior and
existing redirect of <<<"$snapshot".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a3721e31-1916-4da6-978e-6404119df99b

📥 Commits

Reviewing files that changed from the base of the PR and between dc11402 and 491655a.

📒 Files selected for processing (3)
  • .github/workflows/smoke-e2e.yml
  • CHANGELOG.md
  • scripts/smoke-e2e.sh
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/smoke-e2e.yml

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/smoke-e2e.sh">

<violation number="1" location="scripts/smoke-e2e.sh:197">
P2: This `tail | grep -q` check is flaky under `set -o pipefail`; early `grep -q` exit can make `tail` fail with SIGPIPE and trigger a false smoke-test failure.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Comment thread scripts/smoke-e2e.sh Outdated
`tail | grep -q` under `set -o pipefail` can spuriously fail when
`grep -q` exits on first match and `tail` gets SIGPIPE on its next
write. Switch to `grep -q ... < <(tail ...)` so the upstream lives
in its own process and pipefail no longer observes its exit.

Reported by cubic-dev-ai on PR #66.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/smoke-e2e.sh`:
- Around line 59-60: The script sets RUNTIME_DIR and exports TAURI_PILOT_SOCKET
without ensuring the runtime directory exists; update the logic around
RUNTIME_DIR/TAURI_PILOT_SOCKET so the directory is created if missing (e.g.,
mkdir -p "$RUNTIME_DIR" and ensure ownership/permissions as needed) before
forming and exporting TAURI_PILOT_SOCKET, and fail early with a clear error if
the directory cannot be created; apply this change near the RUNTIME_DIR and
TAURI_PILOT_SOCKET assignments in scripts/smoke-e2e.sh.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8e1e5b19-2fe2-48e6-a269-5488d0c07ebf

📥 Commits

Reviewing files that changed from the base of the PR and between 491655a and ed44e79.

📒 Files selected for processing (1)
  • scripts/smoke-e2e.sh

Comment thread scripts/smoke-e2e.sh Outdated
`tauri-plugin-pilot` resolves the Unix socket directory as
XDG_RUNTIME_DIR when private, otherwise /tmp (server/unix.rs:80).
Our script computed RUNTIME_DIR=/run/user/$UID even when the
directory was missing or world-readable — the CLI then tried to
connect to a path the plugin never bound, and the handshake loop
timed out on hosts where systemd-logind has not provisioned the
runtime dir (CodeRabbit #3217860886).

`plugin_socket_dir` now reproduces the same logic in bash: keep
XDG_RUNTIME_DIR when it exists, is owned by the current user and
has no group/world permission bits, else fall back to /tmp.
@mpiton mpiton merged commit aebd2e5 into main May 11, 2026
14 checks passed
mpiton added a commit that referenced this pull request May 11, 2026
* docs: add Sprint 1 quickstart + smoke probe to README (T-135, closes #49)

- Replace generic Setup with "Quickstart" section: clone, install-hooks, `pnpm exec tauri dev` boot flow targeting the ARCHI.md §23 onboarding criterion (< 5 min on a fresh clone).
- Add "Verify the Sprint 1 smoke flow" subsection mirroring `scripts/smoke-e2e.sh` (pilot-gated dev server + `tauri-pilot ipc tasks_list --args '{}'` probe with the v0.5 `--args` form, not the v0.4 positional `'{}'` referenced in the issue body).
- Add "What works in Sprint 1" section enumerating the landed deliverables (hexagonal layout, libsql + V001, `AppContainer::build`, `tasks_list` IPC, `TauriEventPublisher`, `tracing` redaction, Sidebar/WelcomeScreen, Zustand `useTaskStore`, i18n FR+EN, codegen-types, llvm-cov 75 gate, smoke E2E gate) so the README delta after Sprint 0 is visible without diffing CHANGELOG.
- Add explicit link "ARCHI.md §4 — Layout du repo" (GFM anchor `#4-layout-du-repo`) for full architecture rationale.
- Pin `tauri-pilot-cli` install hint to `=0.5.1 --locked` to match `tauri-plugin-pilot = "0.5.1"` in `src-tauri/Cargo.toml` (same hardening rationale as PR #66 CodeRabbit fix).
- Add Smoke E2E badge alongside CI / Release / CodSpeed.
- Update Status line from "Sprint 0 closure" to "Sprint 1 closure — smoke E2E gate green on main".
- Replace pre-existing broken `[AGPL-3.0](LICENSE)` link with the canonical `gnu.org/licenses/agpl-3.0` URL (no `LICENSE` file in repo; matches the badge target). Adding the file itself is out of scope.

CHANGELOG `[Unreleased]` already enumerates Sprint 1 additions (130 entries, verified each spec-required entry present: hexagonal, AppConfig, ensure_dirs, tracing redaction, libsql V001, domain/tasks, EventPublisher, AppContainer, tasks_list, codegen-types, Sidebar/WelcomeScreen/useTaskStore/i18n, UI primitives, llvm-cov gate). No CHANGELOG change needed.

Tests: doc-only (per T-135 spec).

* docs(readme): address PR #67 review (CodeRabbit / cubic / qodo)

- Windows shell note for `./scripts/install-hooks.sh` (run via Git Bash or WSL; PowerShell/cmd.exe will not execute the bash script). Per CodeRabbit comment on L44.
- Reword smoke verification block: explicit "terminal 1" / "terminal 2" labels, drop the ambiguous "instead of" phrasing. Apply CodeRabbit suggested diff on L56 verbatim.
- Correct CI trigger claim: `.github/workflows/smoke-e2e.yml` is `workflow_dispatch` only — README previously said "runs in CI on every push" which is wrong. Per qodo finding on L61.
- Drop links to `ARCHI.md` (`§23` onboarding ref + `§4` architecture link) and `PRD.md` (roadmap ref) because both files are intentionally gitignored at `.gitignore:55-56` ("Specs"). Cubic and qodo correctly flagged the broken internal links. The "Architecture" section now inlines the dependency rule + composition-root invariants that previously lived behind the ARCHI.md §4 link, so the design rationale is still surfaced without depending on a local-only spec file.

T-135 spec mentioned an `ARCHI.md §4` link as a sub-criterion, but that pre-dated the deliberate gitignore policy on the specs files. Honoring the gitignore wins — the AC "no broken markdown link" is the stronger constraint.

---------

Co-authored-by: Mathieu Piton <27002047+mpiton@users.noreply.github.com>
mpiton added a commit that referenced this pull request May 14, 2026
* feat(ci): T-224 Sprint 2 smoke E2E + persistence-after-restart (closes #92)

Add scripts/smoke-sprint-2.sh exercising the full Sprint 2 flow:
boot pnpm tauri dev --features pilot → create project via ProjectTabBar
→ open TaskCreationWizard → drag card from Backlog to Code (with
tasks_move IPC fallback for the @dnd-kit PointerSensor / tauri-pilot
drag event-family mismatch) → kill the dev server via process-group
TERM/KILL → reboot → assert tasks.phase still 'Code' AND the card
re-renders under [data-testid="kanban-column-code-list"].

Workflow .github/workflows/smoke-e2e.yml restructures the single job
into a strategy.matrix.scenario (sprint-1, sprint-2) with fail-fast
disabled; apt install extends to sqlite3 + jq (required by the new
script's sqlite probe + IPC poller); artifact paths and names are
scenario-scoped so each shard uploads only its own bundle.

Hardening reuse from PR #66 / T-134 is verbatim: setsid + negative-
PID kill, plugin_socket_dir() XDG_RUNTIME_DIR fallback, per-lifecycle
LOG_SIZE_BEFORE freshness gate, process-substitution log tail, pinned
tauri-pilot-cli=0.5.1 --locked, redacted *_DISPLAY paths.

Sprint 1 collateral fix: scripts/smoke-e2e.sh#L191's tasks_list
--args '{}' is stale since T-211 added a required project_id: i64;
payload changed to '{"project_id":1}' (Sprint 1 has zero projects so
the WHERE filter still returns []). Without this, the new sprint-1
matrix row would block the AC "workflow green on main".

Adversarial review hardening: poll_ipc_for_phase replaces the
DOM-stability gate before sqlite probes (the optimistic-update path
mutates the DOM before invokeTasksMove resolves); CI=true /
FORGENT_SMOKE_FORCE_WIPE=1 guard prevents local invocations from
wiping the developer's real tasks.db; preflight aborts when another
dev server holds the pilot socket; process-group existence checks
use kill -0 -- "-PGID" so cleanup catches orphaned grandchildren;
TASK_ID validated against the v4 UUID regex before SQL interpolation.

CHANGELOG entry under [Unreleased] Added. README smoke section
points at both smoke-e2e.sh and smoke-sprint-2.sh.

* fix(frontend): T-224 follow-up — IPC camelCase + tauri-pilot 0.5.2 + verified smoke green

Round trip from running `FORGENT_SMOKE_FORCE_WIPE=1 ./scripts/smoke-sprint-2.sh`
locally. The new gate caught three production defects + one upstream
tooling bug that vitest could not surface (mocked `invoke` boundary
hides Tauri 2's camelCase rename, and `pnpm tauri dev` is not part of
the unit-test loop). Smoke now PASSES end-to-end on this commit.

Production fixes:

- `src/shared/ipc/tasks.ts` — `invokeTasksList`, `invokeTasksCreate`,
  `invokeTasksMove` now send the canonical camelCase wire keys
  (`projectId`, `targetPhase`) that Tauri 2's default
  `rename_all = "camelCase"` rewrite of `#[tauri::command]` parameter
  names expects. The previous snake_case payloads failed silently
  with `command X missing required key projectId` / `targetPhase` on
  every real IPC call. Invisible to `vitest` because the test suite
  mocked `@tauri-apps/api/core`'s `invoke` AND asserted the
  (broken) snake_case payload verbatim. `src/shared/ipc/tasks.test.ts`
  assertions + test names + a `/* … */` comment block (oxlint
  `capitalized-comments` on multi-line `//`) updated; 17/17 cases
  pass.

- `scripts/smoke-e2e.sh` — Sprint 1 `tauri-pilot ipc tasks_list`
  payload follows the same camelCase rename. Still returns `[]`
  unchanged (Sprint 1 has zero projects in the wiped DB so the
  `WHERE project_id = ?` filter resolves empty regardless of the
  passed id).

Tauri-pilot upstream bump (mpiton/tauri-pilot#91):

- `src-tauri/Cargo.toml` + `Cargo.lock`: `tauri-plugin-pilot` 0.5.1
  → 0.5.2 via `cargo add tauri-plugin-pilot@0.5.2 --optional`
  (per CLAUDE.md `cargo add` rule, no manual Cargo.toml edits).
- `.github/workflows/smoke-e2e.yml` + `README.md`:
  `cargo install tauri-pilot-cli --version "=0.5.2" --locked`.
- 0.5.2 fixes a long-standing internal-timeout cap on `wait` where
  any `--timeout` above 10000 ms was silently truncated to the
  hardcoded `DEFAULT_TIMEOUT`. We surfaced the bug from the
  Forgent harness — it manifested as "UUID-bearing testids time
  out" because the UUID-bearing `<li>` rows hydrate after API
  state lands, i.e. after the 10 s cap. Reported in
  mpiton/tauri-pilot#91 with the
  comparative selector table, fixed in 0.5.2 by the upstream
  author within hours.

`scripts/smoke-sprint-2.sh` refinements after live runs:

- `boot_app`'s readiness gate switched from
  `[data-testid="kanban-board"]` to `[data-testid="project-tab-bar"]`.
  `<KanbanBoard>` only mounts when `selectedProjectId !== undefined`
  (`App.tsx:177-186`); a fresh DB has zero projects so lifecycle 1
  renders `<TasksEmptyState />` and the old gate would always time
  out. `<ProjectTabBar>` is always rendered on the default
  `route="tasks"`.
- DOM assertions replaced bounded `tauri-pilot wait --selector`
  with `for` loops over `tauri-pilot snapshot` + `grep`. `wait` is
  unreliable when the target sits behind a modal Radix Dialog (the
  TaskCreationWizard lingers a few frames after `onOpenChange(false)`
  and pushes `aria-hidden="true"` onto the background, hiding it
  from the matcher); the accessibility-tree snapshot exposes the
  full DOM regardless of `aria-hidden`.
- Lifecycle-1 Code-column DOM check removed. Issue #92 step 6 only
  requires the sqlite probe (`tasks.phase = 'Code'`); step 7
  reserves the DOM check for after restart. The IPC fallback
  (`tauri-pilot ipc tasks_move`) writes the DB but bypasses the
  Zustand store, so the lifecycle-1 UI does not re-render until a
  refetch trigger fires. Lifecycle 2's cold boot triggers a fresh
  `tasks_list` fetch via T-221, which is where the DOM assertion
  actually belongs per the issue spec.
- `fail()` now dumps a best-effort `tauri-pilot snapshot` to
  `target/smoke-sprint-2-logs/snapshot-on-fail-<N>.txt` for
  post-mortem when the pilot socket is still up.

Verified locally on commit e8ce3c8's branch:
`FORGENT_SMOKE_FORCE_WIPE=1 ./scripts/smoke-sprint-2.sh` exits 0,
produces all three screenshots, confirms `tasks.phase = 'Code'`
post-lifecycle-1 drag, kills + restarts the app, re-verifies DB
+ DOM persistence after T-223/T-221 boot effects restore the
selection.

* fix(frontend): T-224 follow-up — App.test.tsx tasks_list assertions to camelCase

Two regressions surfaced by the full vitest run on top of commit 1dbd149:

- `App — kanban hydration … renders KanbanBoard and invokes tasks_list`
- `App — kanban hydration … refetches tasks_list … when switching projects`

Both asserted the previously-broken snake_case payload (`{ project_id: 1 }`)
that `invokeTasksList` used to send. After the camelCase fix in
`src/shared/ipc/tasks.ts`, the wrappers send `{ projectId: 1 }` so the
assertions had to follow. 333/333 vitest cases now pass.

CHANGELOG entry under T-224 extended with point (f).

* fix(ci): T-224 PR #118 review pass — readme/header copy + lifecycle-aware log

Three minor follow-ups surfaced by the CodeRabbit + cubic-dev-ai pass on PR #118.

- `README.md` (CodeRabbit #3240080125): Sprint 1 quickstart probe
  example `tauri-pilot ipc tasks_list --args '{}'` is stale since
  T-211 added a required `project_id: i64` argument. Tauri 2's
  default `rename_all = "camelCase"` rewrites the wire key to
  `projectId`, so the payload becomes `'{"projectId":1}'`. Sprint 1
  has zero projects in the wiped DB, so the `WHERE project_id = ?`
  filter still returns `[]`.

- `scripts/smoke-e2e.sh:9` (CodeRabbit #3240080129 + cubic-dev-ai
  #3240111500): header comment showed the old positional-JSON form
  `tauri_pilot ipc tasks_list '{"projectId":1}'` (underscore +
  no `--args`). Updated to match the actual 0.5.x invocation:
  `tauri-pilot ipc tasks_list --args '{"projectId":1}'`. No
  behavior change — the body of the script already used the correct
  form at the call site.

- `scripts/smoke-sprint-2.sh:48` (CodeRabbit #3240080133):
  `LOG_FILE="…/forgent.log.$(date -u +%Y-%m-%d)"` was evaluated once
  at script-load time, so a lifecycle-2 boot that crosses UTC
  midnight would stat yesterday's stale path. Replaced with a
  `current_log_file()` helper invoked inside `boot_app` (size-before
  capture) and inside the freshness assertion (post-boot). The
  user-facing `LOG_FILE_DISPLAY` placeholder changed from a baked-in
  date to `<YYYY-MM-DD>` to honestly reflect the lazy resolution.

CHANGELOG entry (g) + (h) appended.

* fix(ci): T-224 PR #118 review pass — UTC roll race between log size capture and grep

cubic-dev-ai PR comment 3241391324 on `scripts/smoke-sprint-2.sh:304`
flagged a residual race after the lazy log-path fix in 00e83a2:

  - `boot_app` captures `wc -c < $(current_log_file)` at start.
  - The dev server boots, the handshake completes (~tens of seconds
    on a cold cargo cache).
  - UTC midnight may roll between those two points. The daily roller
    inside `tracing_appender` opens a new dated file and writes
    `Forgent ready` to it.
  - Post-handshake `current_log_file()` resolves to the NEW path,
    which the byte offset captured at start refers to a DIFFERENT
    file. `tail -c "+SIZE+1"` then either reads past the end of a
    smaller new file (empty output) or skips the `Forgent ready`
    line altogether.

Fix: capture both the path AND the size at start
(`LIFECYCLE_LOG_PATH_AT_START` + `LIFECYCLE_LOG_SIZE_BEFORE`).
Post-handshake, compare `current_log_file()` against the captured
path. If they match (the common case), slice from the recorded
offset. If they diverged (UTC roll happened during boot), read the
new file from byte 0 — its entire content is post-launch by
definition since the roller created it after the script started.

CHANGELOG entry (i) appended under T-224.

---------

Co-authored-by: Mathieu Piton <27002047+mpiton@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[T-134] pnpm tauri dev smoke E2E

1 participant