/ork:dev — Lab-Stack Boot One command boots the four moving parts of a Vercel-Labs-flavored dev loop: 1. portless → named HTTPS (no port collisions across worktrees) 2. emulate → stateful API emulators on the same origin via 3. dev server → / / (auto-detected) 4. agent-browser → pre-warmed session named after the branch State lives in . Teardown via reads the PIDs and signals SIGTERM in reverse boot order. Paired with : the agent-browser session that warms is the same one (and the M125 #2 auto-trigger) attach to — no second startup latency on the first UI test. When to invoke | Situation | Co…

\\t' read -r name pid cmd; do\n [[ -z \"${name}\" ]] && continue\n local_mark=\"✗\"\n if [[ -n \"${pid}\" && \"${pid}\" != \"0\" && \"${pid}\" != \"null\" ]] && kill -0 \"${pid}\" 2>/dev/null; then\n local_mark=\"✓\"\n elif [[ \"${name}\" == \"agentBrowser\" ]]; then\n # Sessions are lazy — sessionName presence is enough to consider them registered.\n local_mark=\"✓\"\n fi\n printf ' %s %-18s %s\\n' \"${local_mark}\" \"${name}\" \"${cmd}\"\ndone \u003c \u003c(jq -r '\n (.processes // {}) | to_entries[]\n | [ .key, (.value.pid // 0 | tostring), (.value.command // .value.sessionName // \"\") ]\n | @tsv\n' \"${STATE_FILE}\")\n\n[[ -n \"${ems}\" ]] && printf ' emulators: %s\\n' \"${ems}\"\nprintf ' base url: %s\\n' \"${base_url}\"\n[[ -n \"${booted}\" ]] && printf ' booted: %s\\n' \"${booted}\"\n\n# Cross-check: portless's own view of the route.\nif command -v portless >/dev/null 2>&1; then\n slug=\"$(jq -r '.subdomain // \"\"' \"${STATE_FILE}\" | sed 's/\\.localhost$//')\"\n if [[ -n \"${slug}\" ]]; then\n if portless list 2>/dev/null | grep -qE \"https?://${slug}\\.localhost(:[0-9]+)?\\b\"; then\n printf ' portless: route registered ✓\\n'\n else\n printf ' portless: route NOT registered (wrapper may have died)\\n'\n fi\n fi\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":2771,"content_sha256":"956e3df471acb2163a0cd2eae5f3754ba387429f8dcda97e9a1a7deccfd40650"},{"filename":"scripts/stop.sh","content":"#!/usr/bin/env bash\n# Generated by OrchestKit Claude Plugin — Created: 2026-04-27\n# /ork:dev stop — tear down this branch's lab stack.\n#\n# Reverse boot order: agent-browser session → portless wrapper (kills wrapped dev server)\n# → emulate. The portless PROXY DAEMON is shared and is NOT stopped here — that's a\n# separate concern (`portless proxy stop` if you really mean it).\n\nset -euo pipefail\n\nPROJECT_DIR=\"${CLAUDE_PROJECT_DIR:-$(pwd)}\"\nSTATE_FILE=\"${PROJECT_DIR}/.claude/state/dev-stack.json\"\n\nif [[ ! -f \"${STATE_FILE}\" ]]; then\n printf 'ork:dev — no state file found, nothing to stop.\\n' >&2\n exit 0\nfi\n\nif ! command -v jq >/dev/null 2>&1; then\n printf 'ork:dev — jq not found. Install: brew install jq\\n' >&2\n exit 1\nfi\n\nsession=$( jq -r '.processes.agentBrowser.sessionName // \"\"' \"${STATE_FILE}\")\nwrap_pid=$(jq -r '.processes.portlessWrapper.pid // 0' \"${STATE_FILE}\")\ne_pid=$( jq -r '.processes.emulate.pid // 0' \"${STATE_FILE}\")\nslug=$( jq -r '.subdomain // \"\"' \"${STATE_FILE}\" | sed 's/\\.localhost$//')\n\nprintf 'ork:dev — sending SIGTERM in reverse boot order…\\n' >&2\n\n# 1. agent-browser — close the session's browser if any.\nif [[ -n \"${session}\" ]]; then\n if AGENT_BROWSER_SESSION=\"${session}\" agent-browser close 2>/dev/null; then\n printf ' ✓ agent-browser session \"%s\" closed\\n' \"${session}\" >&2\n else\n printf ' ⚠ agent-browser session \"%s\" not active\\n' \"${session}\" >&2\n fi\nfi\n\n# 2. portless wrapper — kill its descendants FIRST (the wrapped dev server +\n# any forked node processes), then the wrapper itself. portless doesn't always\n# propagate SIGTERM cleanly, so we walk the process tree explicitly.\nif [[ \"${wrap_pid}\" != \"0\" ]] && kill -0 \"${wrap_pid}\" 2>/dev/null; then\n descendants=()\n queue=(\"${wrap_pid}\")\n while [[ ${#queue[@]} -gt 0 ]]; do\n parent=\"${queue[0]}\"\n queue=(\"${queue[@]:1}\")\n while IFS= read -r kid; do\n [[ -z \"${kid}\" ]] && continue\n descendants+=(\"${kid}\")\n queue+=(\"${kid}\")\n done \u003c \u003c(pgrep -P \"${parent}\" 2>/dev/null || true)\n done\n\n # SIGTERM leaves-first, then the wrapper.\n for ((i=${#descendants[@]}-1; i>=0; i--)); do\n kill -TERM \"${descendants[i]}\" 2>/dev/null || true\n done\n kill -TERM \"${wrap_pid}\" 2>/dev/null || true\n\n for _ in 1 2 3 4 5; do kill -0 \"${wrap_pid}\" 2>/dev/null || break; sleep 1; done\n if kill -0 \"${wrap_pid}\" 2>/dev/null; then\n for d in \"${descendants[@]}\"; do kill -KILL \"${d}\" 2>/dev/null || true; done\n kill -KILL \"${wrap_pid}\" 2>/dev/null || true\n fi\n printf ' ✓ portless wrapper (pid %s) + %d descendant(s) stopped\\n' \\\n \"${wrap_pid}\" \"${#descendants[@]}\" >&2\nfi\n\n# 3. emulate (sidecar)\nif [[ \"${e_pid}\" != \"0\" ]] && kill -0 \"${e_pid}\" 2>/dev/null; then\n kill -TERM \"${e_pid}\" 2>/dev/null || true\n printf ' ✓ emulate (pid %s) stopped\\n' \"${e_pid}\" >&2\nfi\n\n# Note: portless PROXY DAEMON intentionally NOT stopped — it's a shared resource.\n# Verify the route is gone (portless detects wrapper exit and unregisters).\nif [[ -n \"${slug}\" ]] && portless list 2>/dev/null | grep -qE \"https?://${slug}\\.localhost(:[0-9]+)?\\b\"; then\n printf ' ⚠ portless route %s.localhost still registered (proxy may need a moment)\\n' \"${slug}\" >&2\nfi\n\nrm -f \"${STATE_FILE}\"\nprintf 'Cleared %s\\n' \"${STATE_FILE}\" >&2\nprintf '\\nNote: portless proxy daemon left running (shared). Run `portless proxy stop` if you really mean to stop the daemon.\\n' >&2\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3467,"content_sha256":"5484d50a7fe68c4213236bf409bf2d8e9d2ceb2d1dc6b2b93c1b71d073e5b6f5"},{"filename":"test-cases.json","content":"{\n \"skill\": \"dev\",\n \"version\": \"1.0.0\",\n \"testCases\": [\n {\n \"id\": \"trigger-bare\",\n \"rule\": null,\n \"query\": \"/ork:dev\",\n \"expectedBehavior\": [\n \"Triggers the dev skill, not portless or emulate-seed alone\",\n \"Calls scripts/boot.sh\",\n \"Detects package manager from lockfile (pnpm > yarn > bun > npm)\",\n \"Resolves subdomain from current git branch slug\",\n \"Verifies portless + emulate + agent-browser binaries before any side effects\"\n ]\n },\n {\n \"id\": \"trigger-stop\",\n \"rule\": null,\n \"query\": \"/ork:dev stop\",\n \"expectedBehavior\": [\n \"Calls scripts/stop.sh\",\n \"Reads .claude/state/dev-stack.json for tracked PIDs\",\n \"Sends SIGTERM in reverse boot order: agent-browser, dev server, emulate, portless\",\n \"Falls back to SIGKILL after 5 seconds for unresponsive processes\",\n \"Removes the state file last\"\n ]\n },\n {\n \"id\": \"trigger-status\",\n \"rule\": null,\n \"query\": \"/ork:dev status\",\n \"expectedBehavior\": [\n \"Calls scripts/status.sh\",\n \"Probes each tracked PID with kill -0\",\n \"Prints booted-at, branch, baseUrl, and per-process live/dead\",\n \"Exit code 0 if at least one PID is alive, 1 if all dead or no state file\"\n ]\n },\n {\n \"id\": \"rule-prereq-missing\",\n \"rule\": \"lab-stack-prerequisites\",\n \"query\": \"/ork:dev (with portless not installed)\",\n \"expectedBehavior\": [\n \"Detects missing portless binary\",\n \"Prints `✗ portless not found. Install: npm i -g portless`\",\n \"Does NOT start emulate, dev server, or agent-browser\",\n \"Exits 0 (graceful skip, not an error)\"\n ]\n },\n {\n \"id\": \"rule-idempotent-rerun\",\n \"rule\": \"idempotent-boot\",\n \"query\": \"/ork:dev (when stack is already live)\",\n \"expectedBehavior\": [\n \"Reads existing .claude/state/dev-stack.json\",\n \"Probes PIDs via kill -0 to confirm at least one is alive\",\n \"Prints 'already running' message and exits 0\",\n \"Does NOT spawn duplicate processes\"\n ]\n },\n {\n \"id\": \"rule-stale-state\",\n \"rule\": \"idempotent-boot\",\n \"query\": \"/ork:dev (state file exists but all PIDs dead)\",\n \"expectedBehavior\": [\n \"Detects stale state via liveness probe failure\",\n \"Removes .claude/state/dev-stack.json\",\n \"Proceeds with full boot sequence\"\n ]\n },\n {\n \"id\": \"rule-subdomain-slug\",\n \"rule\": \"branch-named-subdomain\",\n \"query\": \"/ork:dev (on branch feat/m125-Lane-B with mixed case + slash)\",\n \"expectedBehavior\": [\n \"Slug becomes feat-m125-lane-b (slashes → dashes, lowercased)\",\n \"DNS hostname is feat-m125-lane-b.localhost\",\n \"Capped at 63 characters\",\n \"agent-browser session name == feat-m125-lane-b\"\n ]\n },\n {\n \"id\": \"ci-escape-hatch\",\n \"rule\": \"lab-stack-prerequisites\",\n \"query\": \"/ork:dev (with CI=1 env)\",\n \"expectedBehavior\": [\n \"Detects CI environment\",\n \"Prints 'CI=1, skipping boot.'\",\n \"Exits 0 immediately, before any prerequisite checks\"\n ]\n },\n {\n \"id\": \"compose-with-emulate-seed\",\n \"rule\": null,\n \"query\": \"/ork:dev (with no emulate.config.yaml)\",\n \"expectedBehavior\": [\n \"Skips emulate up step (no config file present)\",\n \"Suggests running /ork:emulate-seed --auto first\",\n \"Continues to boot portless, dev server, agent-browser without emulators\"\n ]\n },\n {\n \"id\": \"compose-with-expect\",\n \"rule\": null,\n \"query\": \"/ork:expect (after /ork:dev has booted)\",\n \"expectedBehavior\": [\n \"Reads .claude/state/dev-stack.json to find baseUrl + agent-browser session\",\n \"Reuses existing session named after the branch slug (no second handshake)\",\n \"If posttool/ui-change-detector fired before, picks up the route hint\"\n ]\n }\n ]\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":3955,"content_sha256":"48d0a9bd523fe68c696b01996cbbfdef069edcb8bd5c321353ee20d3f0cfd999"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"/ork:dev — Lab-Stack Boot","type":"text"}]},{"type":"paragraph","content":[{"text":"One command boots the four moving parts of a Vercel-Labs-flavored dev loop:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"portless","type":"text","marks":[{"type":"strong"}]},{"text":" → named HTTPS ","type":"text"},{"text":"https://\u003cbranch>.localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":" (no port collisions across worktrees)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"emulate","type":"text","marks":[{"type":"strong"}]},{"text":" → stateful API emulators on the same origin via ","type":"text"},{"text":"@emulators/adapter-next","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"dev server","type":"text","marks":[{"type":"strong"}]},{"text":" → ","type":"text"},{"text":"pnpm dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"npm run dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"yarn dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" (auto-detected)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"agent-browser","type":"text","marks":[{"type":"strong"}]},{"text":" → pre-warmed session named after the branch","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"State lives in ","type":"text"},{"text":".claude/state/dev-stack.json","type":"text","marks":[{"type":"code_inline"}]},{"text":". Teardown via ","type":"text"},{"text":"/ork:dev stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" reads the PIDs and signals SIGTERM in reverse boot order.","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Paired with ","type":"text","marks":[{"type":"strong"}]},{"text":"/ork:expect","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":":","type":"text","marks":[{"type":"strong"}]},{"text":" the agent-browser session that ","type":"text"},{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" warms is the same one ","type":"text"},{"text":"/ork:expect","type":"text","marks":[{"type":"code_inline"}]},{"text":" (and the M125 #2 auto-trigger) attach to — no second startup latency on the first UI test.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to invoke","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Situation","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start work on a new branch","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Resume after a session break","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" (idempotent — skips already-live processes)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tear down before deleting branch","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/ork:dev stop","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inspect state","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/ork:dev status","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Share preview with stakeholder","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/ork:dev --share","type":"text","marks":[{"type":"code_inline"}]},{"text":" (tailnet) or ","type":"text"},{"text":"/ork:dev --funnel","type":"text","marks":[{"type":"code_inline"}]},{"text":" (public)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Time-boxed live demo","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/ork:dev --live 4","type":"text","marks":[{"type":"code_inline"}]},{"text":" (public funnel, 4-hour expiry)","type":"text"}]}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Resuming a backgrounded dev session (CC 2.1.144+):","type":"text","marks":[{"type":"strong"}]},{"text":" Sessions started via ","type":"text"},{"text":"claude --bg","type":"text","marks":[{"type":"code_inline"}]},{"text":" now appear in ","type":"text"},{"text":"/resume","type":"text","marks":[{"type":"code_inline"}]},{"text":" alongside interactive ones, marked ","type":"text"},{"text":"bg","type":"text","marks":[{"type":"code_inline"}]},{"text":" — use ","type":"text"},{"text":"/resume","type":"text","marks":[{"type":"code_inline"}]},{"text":" as the direct recovery path after a crash or session end instead of navigating the agent view.","type":"text"}]},{"type":"paragraph","content":[{"text":"Background shell sessions (CC 2.1.154+):","type":"text","marks":[{"type":"strong"}]},{"text":" In ","type":"text"},{"text":"claude agents","type":"text","marks":[{"type":"code_inline"}]},{"text":", type ","type":"text"},{"text":"! \u003ccommand>","type":"text","marks":[{"type":"code_inline"}]},{"text":" to run a shell command as a backgrounded session you can attach to and detach from — also available as ","type":"text"},{"text":"claude --bg --exec '\u003ccommand>'","type":"text","marks":[{"type":"code_inline"}]},{"text":". Useful for long dev-loop processes (watchers, builds, servers) you want to monitor without holding a terminal.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Modes (M127)","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Flag","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Wraps","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reach","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tailscale CLI","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(none)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"portless \u003cslug> \u003cpkg-mgr> run dev","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://\u003cbranch>.localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":" only","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"not required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--share","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"portless --tailscale ...","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tailnet members on ","type":"text"},{"text":"https://*.ts.net","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--funnel","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"portless --funnel ...","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"public on the internet","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--live N","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"portless --funnel ...","type":"text","marks":[{"type":"code_inline"}]},{"text":" + N-hour expiry","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"public","type":"text","marks":[{"type":"strong"}]},{"text":", tracked in ","type":"text"},{"text":"live-demos.jsonl","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"required","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Tailscale is ","type":"text"},{"text":"optional","type":"text","marks":[{"type":"strong"}]},{"text":" — required only behind ","type":"text"},{"text":"--share","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"--funnel","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"--live","type":"text","marks":[{"type":"code_inline"}]},{"text":". Default ","type":"text"},{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" is unchanged for users who don't share.","type":"text"}]},{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"turbo.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" workspaces is detected (#1562), the boot uses ","type":"text"},{"text":"bare ","type":"text","marks":[{"type":"strong"}]},{"text":"portless","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (zero-config) which auto-discovers each workspace's dev script and assigns subdomains via the task graph. State file shows ","type":"text"},{"text":"mode: \"monorepo\"","type":"text","marks":[{"type":"code_inline"}]},{"text":"; list subdomains via ","type":"text"},{"text":"portless list","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"/ork:dev status","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Boot sequence","type":"text"}]},{"type":"paragraph","content":[{"text":"portless","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a ","type":"text"},{"text":"wrapper","type":"text","marks":[{"type":"strong"}]},{"text":", not a sidecar — ","type":"text"},{"text":"portless \u003cslug> \u003cpkg-mgr> run dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" is one fused command that owns the dev server's lifecycle. ","type":"text"},{"text":"boot.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" tracks the wrapper PID; ","type":"text"},{"text":"stop.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" walks its process tree to clean up children.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"0. Detect package manager pnpm > yarn > bun > npm (lockfile-based)\n1. Resolve subdomain slug \u003cbranch> → lower → / to - → DNS-safe → ≤63 chars\n2. portless proxy start (idempotent — skipped if `portless list` already responds)\n3. emulate --seed \u003cyaml> (sidecar, optional — only if emulate.config.yaml exists)\n4. portless \u003cslug> \u003cpkg-mgr> run dev (FUSED — wrapper owns dev server's lifecycle)\n5. portless get \u003cslug> (poll up to 30s for the route to register)\n6. wait-on \u003cbaseUrl> (poll up to 30s for the dev server through the proxy)\n7. AGENT_BROWSER_SESSION=\u003cslug> agent-browser open \u003cbaseUrl> (warm + register session)\n8. atomic state write (.claude/state/dev-stack.json via jq + temp + mv)\n9. print summary","type":"text"}]},{"type":"paragraph","content":[{"text":"The full annotated walkthrough: ","type":"text"},{"text":"references/boot-sequence.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"State file shape","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"bootedAt\": \"2026-04-27T12:34:56Z\",\n \"branch\": \"feat/m125-lane-b\",\n \"subdomain\": \"feat-m125-lane-b.localhost\",\n \"baseUrl\": \"https://feat-m125-lane-b.localhost:1355\",\n \"mode\": \"single\",\n \"processes\": {\n \"portlessWrapper\": {\n \"pid\": 86104,\n \"command\": \"portless feat-m125-lane-b pnpm run dev\"\n },\n \"agentBrowser\": { \"sessionName\": \"feat-m125-lane-b\" },\n \"emulate\": { \"pid\": 86200, \"command\": \"emulate --seed emulate.config.yaml\" }\n },\n \"emulators\": [\"github\", \"stripe\"],\n \"share\": null,\n \"notes\": \"portless proxy daemon is shared and not tracked here — stop.sh leaves it running.\"\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"--share","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"--funnel","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"--live","type":"text","marks":[{"type":"code_inline"}]},{"text":" is used (M127 #1561 / #1565), ","type":"text"},{"text":"share","type":"text","marks":[{"type":"code_inline"}]},{"text":" becomes:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"\"share\": {\n \"mode\": \"tailscale\",\n \"tailscaleUrl\": \"https://app.your-tailnet.ts.net\",\n \"expiresAt\": \"2026-05-03T20:00:00Z\"\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"mode","type":"text","marks":[{"type":"code_inline"}]},{"text":" is ","type":"text"},{"text":"\"single\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default) or ","type":"text"},{"text":"\"monorepo\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" (when ","type":"text"},{"text":"turbo.json","type":"text","marks":[{"type":"code_inline"}]},{"text":"/workspaces detected). Note ","type":"text"},{"text":"portlessWrapper","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"portless","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"devServer","type":"text","marks":[{"type":"code_inline"}]},{"text":") — portless owns the dev server. Full schema: ","type":"text"},{"text":"references/state-schema.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Auto-surfaced hints (M127)","type":"text"}]},{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" boots, it inspects ","type":"text"},{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" and emits hints:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"@json-render/*","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" detected","type":"text","marks":[{"type":"strong"}]},{"text":" (#1560) → prints the devtools adapter import line so the inspector panel (Spec / State / Actions / Stream / Catalog / Pick) can be enabled in dev. Tree-shakes from production builds.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"@clerk/*","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" detected","type":"text","marks":[{"type":"strong"}]},{"text":" (#1563) → if ","type":"text"},{"text":"clerk","type":"text","marks":[{"type":"code_inline"}]},{"text":" is in ","type":"text"},{"text":"emulate.config.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", prints the mock login URL (","type":"text"},{"text":"http://localhost:4012","type":"text","marks":[{"type":"code_inline"}]},{"text":"); otherwise warns to run ","type":"text"},{"text":"/ork:emulate-seed --auto","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites + graceful no-op","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"$ /ork:dev\n✓ portless found\n✓ agent-browser found\n✓ jq found\n[1] slug feat-m125-lane-b\n\n# OR with a missing prereq:\n✗ portless not found. Install: npm i -g portless\n\nSkipping boot — install missing tools and re-run.","type":"text"}]},{"type":"paragraph","content":[{"text":"portless","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"agent-browser","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" are required. ","type":"text"},{"text":"emulate","type":"text","marks":[{"type":"code_inline"}]},{"text":" is ","type":"text"},{"text":"optional","type":"text","marks":[{"type":"strong"}]},{"text":" — required only if ","type":"text"},{"text":"emulate.config.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" exists. The boot is all-or-nothing on the required set; with no emulate config the boot proceeds without emulators.","type":"text"}]},{"type":"paragraph","content":[{"text":"CI=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" short-circuits the boot (exits 0 immediately).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Status + teardown","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"$ /ork:dev status\nork:dev — feat/m125-lane-b\n ✓ portlessWrapper portless feat-m125-lane-b pnpm run dev\n ✓ agentBrowser feat-m125-lane-b\n base url: https://feat-m125-lane-b.localhost:1355\n booted: 2026-04-27T19:36:54Z\n portless: route registered ✓\n\n$ /ork:dev stop\nork:dev — sending SIGTERM in reverse boot order…\n ✓ agent-browser session \"feat-m125-lane-b\" closed\n ✓ portless wrapper (pid 86104) + 21 descendant(s) stopped\nCleared .claude/state/dev-stack.json\n\nNote: portless proxy daemon left running (shared). Run `portless proxy stop` if you really mean to stop the daemon.","type":"text"}]},{"type":"paragraph","content":[{"text":"Stop walks the wrapper's process tree (","type":"text"},{"text":"pgrep -P","type":"text","marks":[{"type":"code_inline"}]},{"text":" recursively) and SIGTERMs descendants leaves-first because portless doesn't always propagate signals cleanly. The portless proxy daemon itself is shared infrastructure and is ","type":"text"},{"text":"never killed","type":"text","marks":[{"type":"strong"}]},{"text":" by ","type":"text"},{"text":"/ork:dev stop","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Worktree behavior","type":"text"}]},{"type":"paragraph","content":[{"text":"Each git worktree gets its own subdomain — ","type":"text"},{"text":"feat-foo.localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"feat-bar.localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":" coexist on the same machine. The state file lives under each worktree's ","type":"text"},{"text":".claude/state/","type":"text","marks":[{"type":"code_inline"}]},{"text":", so ","type":"text"},{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" from one worktree doesn't see the other's processes.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Idempotency","type":"text"}]},{"type":"paragraph","content":[{"text":"Re-running ","type":"text"},{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" while the stack is already live is a no-op:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"$ /ork:dev\nork:dev — feat/m125-lane-b already running.\n https://feat-m125-lane-b.localhost (uptime 2h 14m)\nRun /ork:dev stop to tear down, or /ork:dev status for detail.","type":"text"}]},{"type":"paragraph","content":[{"text":"Liveness probe: ","type":"text"},{"text":"process.kill(pid, 0)","type":"text","marks":[{"type":"code_inline"}]},{"text":" against each tracked PID. If any are dead, the skill prints which ones and offers to clean up state and reboot.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"How agent-browser composes","type":"text"}]},{"type":"paragraph","content":[{"text":"The session name ","type":"text"},{"text":"equals","type":"text","marks":[{"type":"strong"}]},{"text":" the subdomain — agent-browser commands targeting that session don't need a ","type":"text"},{"text":"--session","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag if it's the only one connected:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"agent-browser open \"https://feat-m125-lane-b.localhost/dashboard\"\n# implicit session = \"feat-m125-lane-b\" because it's the only one","type":"text"}]},{"type":"paragraph","content":[{"text":"/ork:expect","type":"text","marks":[{"type":"code_inline"}]},{"text":" (M125 #2) reads the dev-stack state file and reuses this same session — no second handshake.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Integration with /ork:expect (M125 #2)","type":"text"}]},{"type":"paragraph","content":[{"text":"When auto-expect fires after a ","type":"text"},{"text":".tsx","type":"text","marks":[{"type":"code_inline"}]},{"text":" edit, it:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reads ","type":"text"},{"text":".claude/state/dev-stack.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" to find the agent-browser session and base URL.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Computes the affected route from the file path (","type":"text"},{"text":"app/dashboard/page.tsx","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"/dashboard","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Drives agent-browser against ","type":"text"},{"text":"\u003cbaseUrl>\u003croute>","type":"text","marks":[{"type":"code_inline"}]},{"text":" using the live session.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Records the ARIA snapshot to memory keyed by ","type":"text"},{"text":"(route, parentCommit)","type":"text","marks":[{"type":"code_inline"}]},{"text":" (M125 #6).","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If the dev stack isn't live, auto-expect skips silently — ","type":"text"},{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the prerequisite, not a hard dep.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When NOT to use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CI","type":"text","marks":[{"type":"strong"}]},{"text":" — set ","type":"text"},{"text":"CI=1","type":"text","marks":[{"type":"code_inline"}]},{"text":"; the skill exits 0 without booting.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Production deploys","type":"text","marks":[{"type":"strong"}]},{"text":" — never; this is dev-loop only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Non-Vercel-Labs stacks","type":"text","marks":[{"type":"strong"}]},{"text":" — falls back to install hints; you can still run the underlying tools manually.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Inside a ","type":"text","marks":[{"type":"strong"}]},{"text":"tmux -CC","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" session","type":"text","marks":[{"type":"strong"}]},{"text":" — agent-browser dashboard incompatible with iTerm2 tmux integration.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scripts","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What it does","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/boot.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All-or-nothing prereq check, then 9-step boot. Idempotent (no-ops if already live). Honors ","type":"text"},{"text":"CI=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" to skip in CI.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/stop.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SIGTERM in reverse boot order with 5-second SIGKILL fallback. Removes state file last.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/status.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pretty status. ","type":"text"},{"text":"--quiet","type":"text","marks":[{"type":"code_inline"}]},{"text":" for liveness-only (exit 0 live, 1 down). Used by boot for idempotency.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"/ork:dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" invokes ","type":"text"},{"text":"scripts/boot.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":"; ","type":"text"},{"text":"stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"stop.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":"; ","type":"text"},{"text":"status","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"status.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":". The shell scripts are the source of truth.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/boot-sequence.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step-by-step boot annotated with commands","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/state-schema.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full JSON shape + field semantics","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Rules","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rule","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Impact","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When it applies","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rules/lab-stack-prerequisites.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CRITICAL","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Every boot","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rules/branch-named-subdomain.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HIGH","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Subdomain resolution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rules/idempotent-boot.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HIGH","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Re-running while live","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rules/teardown-order.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MEDIUM","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" invocations","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Running unattended with /goal","type":"text"}]},{"type":"paragraph","content":[{"text":"Set a completion condition with ","type":"text"},{"text":"/goal","type":"text","marks":[{"type":"code_inline"}]},{"text":" (CC 2.1.139+) and this skill will keep working across turns until the condition is met. Works in interactive, ","type":"text"},{"text":"-p","type":"text","marks":[{"type":"code_inline"}]},{"text":", and Remote Control. The overlay panel shows live elapsed / turns / tokens.","type":"text"}]},{"type":"paragraph","content":[{"text":"Example completion condition for this skill:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"/goal until services.running == 4","type":"text"}]},{"type":"paragraph","content":[{"text":"Stops when: all 4 dev-loop services (portless + emulate + dev-server + agent-browser) report healthy on their respective ports/sockets. Compatible with claude.ai Remote Control runs.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Related skills","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/ork:expect","type":"text","marks":[{"type":"code_inline"}]},{"text":" — diff-aware browser tests; reuses the agent-browser session this skill warms","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"/ork:emulate-seed","type":"text","marks":[{"type":"code_inline"}]},{"text":" — generates the emulator config that step 3 consumes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"portless","type":"text","marks":[{"type":"code_inline"}]},{"text":" (skill) — underlying tool docs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"browser-tools","type":"text","marks":[{"type":"code_inline"}]},{"text":" (skill) — agent-browser command reference","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"dev","tags":["dev-loop","portless","emulate","agent-browser","vercel-labs","lab-stack","m125","m127"],"author":"@skillopedia","source":{"stars":180,"repo_name":"orchestkit","origin_url":"https://github.com/yonatangross/orchestkit/blob/HEAD/src/skills/dev/SKILL.md","repo_owner":"yonatangross","body_sha256":"d13202099792b54dc86e1be5cefe7c53234fc50c8d1853fa6ff8982ab8c1c2fb","cluster_key":"709c160c2b407b61f82f275cf99d8ad90669f0f1dbc6e5a5fea86988997ea5f0","clean_bundle":{"format":"clean-skill-bundle-v1","source":"yonatangross/orchestkit/src/skills/dev/SKILL.md","attachments":[{"id":"27808e0e-d6a0-5bae-bfab-9b96b06324ea","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/27808e0e-d6a0-5bae-bfab-9b96b06324ea/attachment.md","path":"references/boot-sequence.md","size":7469,"sha256":"246483d14b7ec15d86c1fe4552c45ae2b35110f72835a46d43270d8e1ab3a252","contentType":"text/markdown; charset=utf-8"},{"id":"cb6f564a-87b0-5a74-bb72-5a8d418681a4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cb6f564a-87b0-5a74-bb72-5a8d418681a4/attachment.md","path":"references/state-schema.md","size":4192,"sha256":"61e2096aca2092038a1966ee481ab77e0e93e6612afc7c4c12effbcbff6f3c6b","contentType":"text/markdown; charset=utf-8"},{"id":"b75f2a09-c5cd-51e5-aa1d-8279aa32acce","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b75f2a09-c5cd-51e5-aa1d-8279aa32acce/attachment.md","path":"rules/_sections.md","size":1033,"sha256":"6dc407dc755dc953d624e97775b8065a854ed208e23dc4636bf37dc0a73174b0","contentType":"text/markdown; charset=utf-8"},{"id":"b173b128-2a95-5220-8a86-47c365b32f6b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b173b128-2a95-5220-8a86-47c365b32f6b/attachment.md","path":"rules/_template.md","size":339,"sha256":"1f6b17cb8c02a543b46a648f062ff0a0c93635b93d51663bc299033be4dc3f83","contentType":"text/markdown; charset=utf-8"},{"id":"f4cd429b-457b-5b9a-921b-1d7002b15f34","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f4cd429b-457b-5b9a-921b-1d7002b15f34/attachment.md","path":"rules/branch-named-subdomain.md","size":1514,"sha256":"fd283346122f311b7d0f3868cc173ddf170c521bdbf29165169204d47122d363","contentType":"text/markdown; charset=utf-8"},{"id":"17198f3c-9e60-5bc1-bbfe-ff4acb0bd9c9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/17198f3c-9e60-5bc1-bbfe-ff4acb0bd9c9/attachment.md","path":"rules/idempotent-boot.md","size":1730,"sha256":"c5e735ba406874a0e0e0e8619fbe1af4e9d3cc8cc48d93d6c2972f224de0d06f","contentType":"text/markdown; charset=utf-8"},{"id":"96f64ee5-7469-50fc-b621-635974e85d49","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/96f64ee5-7469-50fc-b621-635974e85d49/attachment.md","path":"rules/lab-stack-prerequisites.md","size":2008,"sha256":"3523f7541880cbedd67742482386da47a00ffde794eb446a2602a9f7c019b58b","contentType":"text/markdown; charset=utf-8"},{"id":"dbeccaec-96e3-5bd5-9148-6bf50400d41f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dbeccaec-96e3-5bd5-9148-6bf50400d41f/attachment.md","path":"rules/teardown-order.md","size":1839,"sha256":"b90f754a2a57bfd76b840462252ce3bbcb52b79ac7fe3ef483251b2d251cc2f3","contentType":"text/markdown; charset=utf-8"},{"id":"0157ea44-f166-54d0-b1a2-d4ae9876f232","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0157ea44-f166-54d0-b1a2-d4ae9876f232/attachment.sh","path":"scripts/boot.sh","size":17556,"sha256":"0daed456c42b190050969e64e897bd006ea7c5e61e8485bbaf32cd456e39dd35","contentType":"application/x-sh; charset=utf-8"},{"id":"1d587437-e508-5a9f-b9b5-79b9e36558a2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1d587437-e508-5a9f-b9b5-79b9e36558a2/attachment.sh","path":"scripts/status.sh","size":2771,"sha256":"956e3df471acb2163a0cd2eae5f3754ba387429f8dcda97e9a1a7deccfd40650","contentType":"application/x-sh; charset=utf-8"},{"id":"b4b6d93e-9633-5379-b224-b59f72ecf840","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b4b6d93e-9633-5379-b224-b59f72ecf840/attachment.sh","path":"scripts/stop.sh","size":3467,"sha256":"5484d50a7fe68c4213236bf409bf2d8e9d2ceb2d1dc6b2b93c1b71d073e5b6f5","contentType":"application/x-sh; charset=utf-8"},{"id":"b12c7600-2033-59b5-9b12-196bfd70f8cc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b12c7600-2033-59b5-9b12-196bfd70f8cc/attachment.json","path":"test-cases.json","size":3955,"sha256":"48d0a9bd523fe68c696b01996cbbfdef069edcb8bd5c321353ee20d3f0cfd999","contentType":"application/json; charset=utf-8"}],"bundle_sha256":"f28edde0db9010facf47a5e9897dbec97255b9ef0f41a5b0af704595a4fb33f7","attachment_count":12,"text_attachments":12,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":2,"skill_md_path":"src/skills/dev/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"browser-automation-scraping","category_label":"Browser"},"exact_dupes_collapsed_into_this":1},"context":"inherit","version":"v1","category":"browser-automation-scraping","metadata":{"category":"devops","milestone":"M127","upstream-packages":["portless","emulate","agent-browser","tailscale"]},"complexity":"medium","import_tag":"clean-skills-v1","description":"One-command dev loop boot. Spins up portless (named HTTPS subdomain), emulate (stateful API mocks), the project's dev server, and an agent-browser session — all using the current git branch as the namespace key. Replaces the 4-terminal manual setup with a single `/ork:dev` invocation. Use when starting a new feature branch, switching worktrees, or returning to a project after a break. Skip silently when prerequisite binaries (portless, emulate, agent-browser) are missing — emits install hints.","argument-hint":"[start|stop|status] [--share|--funnel|--live H]","compatibility":"Claude Code 2.1.148+","user-invocable":true,"persuasion-type":"guidance","disable-model-invocation":false}},"renderedAt":1782986224003}

/ork:dev — Lab-Stack Boot One command boots the four moving parts of a Vercel-Labs-flavored dev loop: 1. portless → named HTTPS (no port collisions across worktrees) 2. emulate → stateful API emulators on the same origin via 3. dev server → / / (auto-detected) 4. agent-browser → pre-warmed session named after the branch State lives in . Teardown via reads the PIDs and signals SIGTERM in reverse boot order. Paired with : the agent-browser session that warms is the same one (and the M125 #2 auto-trigger) attach to — no second startup latency on the first UI test. When to invoke | Situation | Co…