feat(ci): Sprint 1 smoke E2E + tauri-pilot wiring (T-134)#66
Conversation
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).
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR implements the Sprint 1 smoke E2E acceptance gate: adds an optional Cargo feature ChangesSmoke E2E Testing Infrastructure
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)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Smoke E2E — local validation resultRan Screenshot ( |
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.
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
.github/workflows/smoke-e2e.ymlCHANGELOG.mdscripts/smoke-e2e.shsrc-tauri/.gitignoresrc-tauri/Cargo.tomlsrc-tauri/build.rssrc-tauri/src/lib.rs
`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.
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.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
scripts/smoke-e2e.sh (1)
156-159: 💤 Low valueConsider
-Eflag for grep portability.The
\|alternation syntax is GNU grep-specific; BSD grep (macOS) requires-Ewith|. 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
📒 Files selected for processing (3)
.github/workflows/smoke-e2e.ymlCHANGELOG.mdscripts/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
There was a problem hiding this comment.
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.
`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.
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
scripts/smoke-e2e.sh
`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.
* 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>
* 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>
Closes #48
Summary
Sprint 1 acceptance gate (T-134). Adds a runnable smoke E2E that boots
pnpm tauri dev, drives the app throughtauri-pilot, and asserts the five Sprint 1 ACs. Wirestauri-plugin-pilotbehind an opt-inpilotcargo 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_listIPC (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. Bootspnpm tauri dev --features pilot, pollstauri-pilot ping(default 180 s / 300 s CI), then asserts:<aside>mounted in the DOM snapshot (locale-agnostic — accepts ENMain navigation, FRNavigation principale, or the structuraldata-testid="usage-indicator")tauri-pilot ipc tasks_list --args '{}'returns[]<repo>/.claude/forgent/tasks.dbexists~/.forgent/logs/forgent.log.YYYY-MM-DDcontainsForgent readytarget/smoke-screenshot.pngnon-empty.github/workflows/smoke-e2e.yml—workflow_dispatch-only Linux job. Installstauri-pilot-cli+ xvfb, runs the smoke underxvfb-run, uploads the screenshot artifact on success andtarget/smoke-logs/on failure. Sprint 1 sign-off accepts the manual local run; full matrix lands in Sprint 10.src-tauri/Cargo.toml—tauri-plugin-pilot = "0.5.1"(crates.io, not git) added as--optional. Newpilotfeature toggles the plugin + capability emission.src-tauri/build.rs— emitscapabilities/pilot.json(grantingpilot:defaulton the main window) iffcfg!(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 oftauri_plugin_log(see fix below).src-tauri/.gitignore— ignores the emittedcapabilities/pilot.json.CHANGELOG.md—[Unreleased] Addedentry with full rationale.Bugs surfaced + fixed
tracingsubscriber double-init panic (T-120 regression).tauri_plugin_log::Builder::default().build()installed a globaltracingsubscriber at plugin build time, thenbootstrap()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 viacargo remove tauri-plugin-log.src-tauri/instead of repo root.AppConfig::load()usescurrent_dir(), buttauri-clicds intosrc-tauri/before invokingcargo run. The smoke script now exportsFORGENT_PROJECT_ROOT="$REPO_ROOT"before launching the dev server so AC4 (DB at<project>/.claude/forgent/tasks.db) passes.Acceptance criteria
pnpm tauri devopens a windowtauri-pilot ipc tasks_list --args '{}'returns[]<project>/.claude/forgent/tasks.dbexists~/.forgent/logs/forgent.log.YYYY-MM-DDexists withForgent readylineTest plan
Verified locally on 2026-05-11:
cargo clippy --workspace -- -D warnings— clean (5 pre-existing ts-rstransparentparse warnings, none new)cargo test --workspace --lib— 169/169 passcargo deny check— advisories ok, bans ok, licenses ok, sources okpnpm exec tsc -b— cleanpnpm exec oxlint --max-warnings=0 .— 0 warnings / 0 errors across 39 filespnpm exec vitest run— 106/106 passScreenshot
Summary by CodeRabbit
Tests
Chores
Bug Fixes
Documentation