Skip to content

feat: project run skill for dev and isolated lab launches#36

Merged
ElbertePlinio merged 5 commits into
mainfrom
feat/run-skill
Jul 10, 2026
Merged

feat: project run skill for dev and isolated lab launches#36
ElbertePlinio merged 5 commits into
mainfrom
feat/run-skill

Conversation

@ElbertePlinio

Copy link
Copy Markdown
Member

Adds a project run skill so any agent can launch PickScribe — normal dev or an isolated headless lab instance — without rediscovering the launch quirks: Wayland leak isolation (env -u WAYLAND_DISPLAY GDK_BACKEND=x11), .labtest single-instance identifier override with separate Vite on a scanned free port, private D-Bus session for the tray icon, Xvfb + xfwm4, inspect-then-kill pid cleanup (no pkill -f in compound commands).

Canonical skill at .agents/skills/run/SKILL.md, with .claude/skills/run as a relative symlink for Claude Code discovery.

Tested (by the builder, transcript kept): bun install --frozen-lockfile; Vite 200 on port 1421, stopped by inspected pid; full lab boot on Xvfb :97 with the labtest identifier; dashboard screenshot captured and visually inspected; all five lab processes stopped by inspected pids.

Known limitation (documented in the skill): XDG config/data dirs are not keyed on the identifier, so a lab instance sees real user data — the skill forbids destructive UI actions.

Not release-note-worthy: agent tooling only, no app changes (docs/releases/UNRELEASED.md untouched).

Closes #35
Part of the agent-DX batch: pickforge/pickforge-platform#19

Encodes the launch quirks agents kept relearning: Wayland leak (GDK_BACKEND=x11 + unset WAYLAND_DISPLAY), labtest single-instance identifier, private D-Bus for the tray, Xvfb+xfwm4, real-user-data warning (XDG dirs not keyed on identifier), inspect-then-kill pid cleanup. Canonical in .agents/skills/run with a .claude/skills symlink.

Refs #35
@ElbertePlinio

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: bd5302e49e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd5302e49e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .agents/skills/run/SKILL.md Outdated
for candidate in {1421..1439}; do ! ss -ltnH "sport = :$candidate" | grep -q . && { PORT=$candidate; break; }; done
: "${PORT:?No free port in 1421-1439}"

bun run dev -- --port "$PORT" --strictPort >"/tmp/pickscribe-vite-$PORT.log" 2>&1 &

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Install dependencies before the lab launch

In a fresh checkout or agent container where setup scripts have not run, the isolated lab workflow can be the first path used for screenshots; because this section starts with bun run dev and never runs bun install --frozen-lockfile, the local vite/tauri binaries are missing and the launch fails before the lab starts. The normal-dev section includes the install, but the lab workflow is presented independently, so agents following it cannot launch the isolated instance from a clean repo.

Useful? React with 👍 / 👎.

Comment thread .agents/skills/run/SKILL.md Outdated
Pick an unused display and frontend port, then start Vite separately:

```bash
DISPLAY_NUM=97

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pick an actually unused X display

When another lab run or X server is already using :97, this fixed assignment lets Xvfb fail while the later DISPLAY=:$DISPLAY_NUM app and screenshot commands still target that existing display (or fail with no clear cause). The section says to pick an unused display, so the snippet should scan/reserve a free display instead of hard-coding one that concurrent agents can collide on.

Useful? React with 👍 / 👎.


```bash
DISPLAY_NUM=97
for candidate in {1421..1439}; do ! ss -ltnH "sport = :$candidate" | grep -q . && { PORT=$candidate; break; }; done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fail when the port scanner is unavailable

In minimal agent images where ss/iproute2 is not installed, this negated pipeline treats the ss: command not found case as “no listener” because grep sees no input, so it blindly selects 1421 instead of failing or using another probe. If that port is already occupied, the subsequent bun run dev -- --port "$PORT" --strictPort exits and the lab never starts, even though the workflow claims it scanned for a free port.

Useful? React with 👍 / 👎.

Lab launches now override HOME + XDG dirs to a fresh temp profile (CARGO_HOME/RUSTUP_HOME kept real so tauri dev still compiles), matching the sibling apps after their reviews proved the labtest identifier alone leaks real user data. Verified: lab created its own pickscribe.db and WebKit storage under the temp home, real config/data mtimes unchanged. Whisper models are an explicit opt-in symlink. Free X display scanned instead of hard-coded; lab home removed in cleanup.
@ElbertePlinio

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2b2587c29

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

GDK otherwise prefers Wayland: `DISPLAY=:N` alone can open on the live desktop. The app has a tray icon, so give it a private D-Bus session. `REAL_HOME` preserves the existing Cargo and Rustup installations:
```bash
dbus-run-session -- env -u WAYLAND_DISPLAY GDK_BACKEND=x11 DISPLAY=":$DISPLAY_NUM" \
HOME="$LAB_HOME" XDG_CONFIG_HOME="$LAB_HOME/.config" XDG_DATA_HOME="$LAB_HOME/.local/share" XDG_CACHE_HOME="$LAB_HOME/.cache" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Disable real-session side effects in the lab profile

When this command points HOME/XDG at an empty profile, PickScribe falls back to its defaults (paste.method = auto, copy_to_clipboard = true, and local_only = false in src/config.rs) while still inheriting the caller's process environment. In the documented dictation-test path on a Wayland developer machine with the recommended ydotool service and/or cloud API keys present, stopping dictation can paste through global input automation into the user's real focused app and can send cleanup text to a cloud provider, even though the skill describes the lab as isolated. Seed $LAB_HOME/.config/pickscribe/config.toml with paste disabled and local-only enabled, or explicitly unset the relevant API/paste environment before launching.

Useful? React with 👍 / 👎.

Comment thread .agents/skills/run/SKILL.md Outdated
```
Start an Xvfb display and a window manager. `xfwm4` is required for maximize/restore controls:
```bash
Xvfb ":$DISPLAY_NUM" -screen 0 1440x1000x24 -nolisten tcp >"/tmp/pickscribe-xvfb-$DISPLAY_NUM.log" 2>&1 &

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Persist lab variables across command snippets

The lab setup assigns DISPLAY_NUM, PORT, LAB_HOME, and REAL_HOME only in the first fenced command block, but the later Xvfb, Tauri, screenshot, and cleanup snippets rely on those shell variables. When an agent follows the skill with separate exec_command calls (the normal Codex/Claude workflow unless a persistent TTY is explicitly opened), this line runs with an empty display (Xvfb ":"), and the later launch/cleanup commands similarly lose the Vite port and lab home. Persist the values in an env file that each snippet sources, or present the lab flow as one script/session.

Useful? React with 👍 / 👎.

Basic boot needs no real-profile files. To test dictation, opt in to model-only access before launch:
```bash
mkdir -p "$LAB_HOME/.local/share/whisper.cpp"
ln -s "$REAL_HOME/.local/share/whisper.cpp/models" "$LAB_HOME/.local/share/whisper.cpp/models"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve access to the real whisper-cli for lab dictation

This opt-in only links the real model directory into the lab home. In the common installed setup fixed by src/engine/mod.rs, whisper-cli may live in the real ~/.local/bin and not be on PATH; after the lab launch changes HOME to $LAB_HOME, find_command falls back to $LAB_HOME/.local/bin instead, so dictation tests still fail with “whisper-cli not found” even though the models were linked. Add the real ~/.local/bin to the lab PATH or link the CLI into $LAB_HOME/.local/bin along with the models.

Useful? React with 👍 / 👎.

Comment thread .agents/skills/run/SKILL.md Outdated
for candidate in {1421..1439}; do ! ss -ltnH "sport = :$candidate" | grep -q . && { PORT=$candidate; break; }; done
: "${PORT:?No free port in 1421-1439}"

bun run dev -- --port "$PORT" --strictPort >"/tmp/pickscribe-vite-$PORT.log" 2>&1 &

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Install dependencies before starting the lab frontend

The lab path starts directly with bun run dev, but a clean checkout has no node_modules and this package script depends on Vite from devDependencies; the normal launch includes bun install --frozen-lockfile, while the lab section does not. Agents that choose the isolated lab flow first therefore fail before Tauri starts with the frontend command missing. Add the install step to the lab setup or explicitly require it before this command.

Useful? React with 👍 / 👎.


Find each PID first with `pgrep -f` (bracketed patterns avoid matching the search). Confirm it is the lab, then run the matching `bash` kill command separately:
```bash
pgrep -af "$PWD/target/debug/[p]ickscribe-app"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope cleanup to the lab app process

This pgrep matches every pickscribe-app binary built from the current workspace, not just the lab instance. If a normal dev instance is running from the same checkout at the same time (the lab identifier allows both to coexist), the output does not show DISPLAY or $LAB_HOME, so the following kill instruction can terminate the real dev app. Filter by the lab process environment/parent Tauri PID before presenting the PID to kill.

Useful? React with 👍 / 👎.

Seeds a safe lab config (local_only, paste off) and unsets cloud API keys so an empty lab profile cannot paste into the real session or call cloud APIs; whisper-cli/models are an explicit opt-in; lab state persists via /tmp/pickscribe-lab.env for separate shell calls; ss guarded; Xvfb waited on; app kill gated on lab environ; lab identifier suffixed with display number to avoid concurrent-lab single-instance pings. Verified end-to-end: LOCAL-ONLY badge active in the lab screenshot, empty profile, clean teardown.
@ElbertePlinio

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb57033083

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .agents/skills/run/SKILL.md Outdated
: "${PORT:?No free port in 1421-1439}"
mkdir -p "$LAB_HOME/.config/pickscribe"
printf '%s\n' '[general]' 'local_only = true' '' '[paste]' 'method = "none"' 'copy_to_clipboard = false' > "$LAB_HOME/.config/pickscribe/config.toml"
printf 'REAL_HOME=%s\nPORT=%s\nDISPLAY_NUM=%s\nLAB_HOME=%s\n' "$REAL_HOME" "$PORT" "$DISPLAY_NUM" "$LAB_HOME" > /tmp/pickscribe-lab.env

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use a per-lab env file instead of a shared path

When two agents follow this skill concurrently, the second setup overwrites /tmp/pickscribe-lab.env, so the first agent's later start/screenshot/cleanup commands source the wrong LAB_HOME, PORT, and DISPLAY_NUM. That defeats the documented concurrent-lab support and can make an agent inspect or kill another lab while leaving its own processes behind; write the env file under the lab directory or include a unique suffix and use that path consistently.

Useful? React with 👍 / 👎.

Comment thread .agents/skills/run/SKILL.md Outdated
[ -e "/tmp/.X11-unix/X$DISPLAY_NUM" ] || { echo "Xvfb did not start" >&2; false; }
setsid xfwm4 --display=":$DISPLAY_NUM" --compositor=off </dev/null >"/tmp/pickscribe-xfwm-$DISPLAY_NUM.log" 2>&1 &
setsid dbus-run-session -- env -u WAYLAND_DISPLAY -u DEEPSEEK_API_KEY -u OPENAI_API_KEY -u OLLAMA_API_KEY -u PICKSCRIBE_API_KEY GDK_BACKEND=x11 DISPLAY=":$DISPLAY_NUM" \
HOME="$LAB_HOME" XDG_CONFIG_HOME="$LAB_HOME/.config" XDG_DATA_HOME="$LAB_HOME/.local/share" XDG_CACHE_HOME="$LAB_HOME/.cache" CARGO_HOME="$REAL_HOME/.cargo" RUSTUP_HOME="$REAL_HOME/.rustup" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Isolate the recorder state directory for labs

When a lab is used for dictation or file transcription while the real app (or another lab) is running, this launch still inherits PICKSCRIBE_STATE_DIR/XDG_RUNTIME_DIR; recorder::state_dir() then writes temp WAVs/logs under the real runtime directory instead of $LAB_HOME. Live recording filenames use only second granularity, so the lab can share or truncate the same state files as another instance and corrupt the experiment; set PICKSCRIBE_STATE_DIR to a lab-owned path while preserving the real runtime dir for PipeWire.

Useful? React with 👍 / 👎.

Comment thread .agents/skills/run/SKILL.md Outdated
Basic boot needs no real-profile files. To test dictation, opt in to model and whisper-cli access before launch:
```bash
source /tmp/pickscribe-lab.env
WHISPER_CLI=$(command -v whisper-cli) || { echo "whisper-cli required" >&2; false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Fall back to the standard local whisper-cli path

When whisper-cli was installed by the repo's local installer but $REAL_HOME/.local/bin is not on the agent shell PATH, this command aborts even though the app itself supports that standard location. That prevents lab dictation testing in an otherwise supported setup; try command -v and then $REAL_HOME/.local/bin/whisper-cli/whisper.cpp before failing.

Useful? React with 👍 / 👎.

Comment thread .agents/skills/run/SKILL.md Outdated
```bash
source /tmp/pickscribe-lab.env
setsid bun run dev -- --port "$PORT" --strictPort </dev/null >"/tmp/pickscribe-vite-$PORT.log" 2>&1 &
for _ in {1..60}; do curl -fsS "http://127.0.0.1:$PORT/" >/dev/null && break; sleep 1; done; curl -fsS "http://127.0.0.1:$PORT/" >/dev/null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop the launch recipe when readiness checks fail

When Vite never becomes ready (or curl is unavailable), the final curl here returns non-zero but this fenced shell block has no set -e and no || exit, so it continues to start Xvfb and Tauri against an unavailable dev server. The same pattern makes failed prerequisite guards continue, leaving confusing half-started lab processes; make the readiness checks abort the recipe before launching later processes.

Useful? React with 👍 / 👎.

Lab state moves to $PWD/.lab.env (gitignored, one lab per checkout); PICKSCRIBE_STATE_DIR points recorder temp WAVs/logs at the lab home (verified the env var in src/engine/recorder.rs); launch block fails fast when Vite never becomes ready; whisper-cli opt-in falls back to ~/.local/bin when not on PATH.
@ElbertePlinio

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83923ae682

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

for candidate in {1421..1439}; do ! ss -ltnH "sport = :$candidate" | grep -q . && { PORT=$candidate; break; }; done
: "${PORT:?No free port in 1421-1439}"
mkdir -p "$LAB_HOME/.config/pickscribe"
printf '%s\n' '[general]' 'local_only = true' '' '[paste]' 'method = "none"' 'copy_to_clipboard = false' > "$LAB_HOME/.config/pickscribe/config.toml"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid clipboard-only mode in the lab seed config

For lab runs that exercise dictation, this seed does not actually disable output: method = "none" is the app's Clipboard-only mode, and the GUI path calls paste::deliver, which copies whenever cfg.method != "type" even if copy_to_clipboard is false (src/engine/paste.rs:53). Because the lab launch keeps the user's runtime/PATH while only unsetting WAYLAND_DISPLAY, environments with a clipboard helper such as wl-copy can still write dictated text to the live clipboard, contradicting the safe-profile/paste-through guidance; the skill needs a true no-copy/no-paste path or an explicit warning not to test dictation this way.

Useful? React with 👍 / 👎.

method=none is clipboard-only mode and copy_to_clipboard still runs; wl-copy finds the real compositor through the default socket in the inherited runtime dir even with WAYLAND_DISPLAY unset. The lab now gets a private XDG_RUNTIME_DIR (also blocks ydotool), verified by booting the lab and checking the app's environ. Prepare refuses to overwrite live lab state; cleanup guards on missing state.
@ElbertePlinio

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: bb679b2f0c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ElbertePlinio ElbertePlinio merged commit 2632032 into main Jul 10, 2026
1 check passed
@ElbertePlinio ElbertePlinio deleted the feat/run-skill branch July 10, 2026 00:52
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.

Agent DX: project run skill (dev launch + headless verify)

1 participant