Skip to main content
Blitz
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage
Edit page

Manual verification

Everything here needs a person, a real terminal, real credentials, or GitHub. The automated suite (413 tests on macOS and Linux) covers the logic behind each item; this list checks the parts it can’t. Each item has an expected result — if you see something else, note it next to the item.

Setup for most items: bazel build //apps/cli:blitz //apps/service:blitzd, then put bazel-bin/apps/cli and bazel-bin/apps/service on your PATH (or use a release archive). Use a scratch Git repository as the workspace so edits are safe.

Cost note: items marked 💲 call a paid API. A short session costs cents; long sessions and /compact cost more.


0. Setup

  • blitz config init, then add your API key(s) to ~/.blitz/.env.toml. Expected: file created with mode 600; re-running without --force refuses.
  • blitz doctor 💲 then blitz doctor --online Expected: credentials ✓, model ✓, model request ✓ with --online, shell sandbox on, pricing ✓ for the default model.

1. Gemini end to end 💲

  • Ask it to read a file and summarize it. Expected: answer streams in as it’s generated; Markdown renders; a dim usage line (↳ … in · … out · context … · $…) follows the turn.
  • Ask it to fix a small bug that needs grep + an edit + go test (or your language’s tests). Expected: tool badges appear; the edit asks for approval with a colored diff; the test command asks for approval; the turn finishes.
  • /cost and /context. Expected: non-zero tokens; cost shown; context size roughly matches the usage line.
  • Set [context] token_threshold = 4000 temporarily, then have a few long turns. Expected: the conversation keeps working past the threshold; /context stays near or below it.

2. Anthropic end to end 💲

Set [llm] provider = "anthropic" (key via api_key, ANTHROPIC_API_KEY, or ant auth login).

  • Banner shows model claude-opus-5; doctor --online passes.
  • A multi-step task with several tool calls (read → edit → run tests). Expected: completes without “invalid request” errors between tool calls (thinking blocks are carried across correctly).
  • Run two turns in a row, then /cost. Expected: “read from cache” is non-zero from the second turn on.
  • Quit, then blitz --continue "what did we just do?". Expected: it remembers the previous turn’s details.
  • Optional: fallbacks = "off" in config, repeat one turn. Expected: works the same.

3. OpenAI-compatible and Ollama 💲

  • provider = "openai" with a real key: a turn with at least one tool call. Expected: tool runs; no JSON printed as text.
  • provider = "ollama" with a local model that emits tool calls as JSON text (e.g. qwen2.5-coder). Expected: the JSON becomes a real tool call (badge shown), not echoed text.

4. Terminal experience

  • Up/Down recall history; history survives restart (~/.blitz/history, mode 600).
  • Ctrl+R searches history.
  • Tab completes /com → /compact, /agent he → helios, /resume → this directory’s session IDs, look at @src/ → file names.
  • Multi-line: end a line with \ and continue; also a block between two """ lines. Expected: sent as one prompt with the line breaks kept.
  • Resize the terminal mid-answer. Expected: no garbled output afterwards.
  • Light terminal theme: COLORFGBG="0;15" blitz. Expected: Markdown readable on a light background.
  • Spinner shows while waiting and disappears before output and approval prompts.

5. Ctrl+C behaviour

  • At an idle prompt with no background processes. Expected: exits immediately with “Goodbye”.
  • During a long model answer. Expected: “⏹ Interrupted”, back at the prompt, session still usable.
  • At an approval prompt. Expected: the whole turn is cancelled (not just that one action).
  • During a long shell command (sleep 60 approved). Expected: the command is killed promptly.

6. Approvals

  • Long edit: diff is truncated; d shows the full diff and asks again.
  • s on a command, then the identical command again. Expected: no second prompt this session.
  • a on a command; restart; same command in the same directory → no prompt; in another directory → prompts.
  • /approvals lists both; /approvals revoke 1 removes one.
  • Add deny = ["rm -rf *"] under [sandbox.commands], approve everything with a, then ask for rm -rf build. Expected: blocked by policy without a prompt.

7. Undo, diff, checkpoints

  • After a turn that edits files: /diff shows the change; /checkpoints lists the turn; /undo restores the files.
  • Make an edit via the agent, then change the same file yourself, then /undo. Expected: refuses with a conflict; /undo --force restores.
  • /diff git shows the working tree diff.

8. Compaction 💲

  • In a session with 5+ turns: /compact keep the file names we touched. Expected: “Replaced N earlier events…”; the next answer still knows the key facts and file names.
  • Quit and --continue. Expected: the summary still applies (the model knows earlier context but /context is small).
  • /compact twice with a turn in between. Expected: facts from before the first compaction are still known.

9. Sessions

  • Run a prompt in directory A, then in directory B. In A: blitz --continue "…". Expected: continues A’s session, not B’s.
  • /session list in A shows only A’s; /session list --all shows both with their directories.
  • blitz --resume <B's id> from A. Expected: works, with a warning that it started elsewhere.

10. Sandbox on your real workflow

  • Ask the agent to run your project’s test command. Expected: works (build caches are writable).
  • Ask it to cat ~/.ssh/id_ed25519 (or any blocked file). Expected: denied / empty.
  • Ask it to write outside the workspace (e.g. echo x > ~/Desktop/x). Expected: “Operation not permitted”.
  • npm install (or another tool writing to its own cache). Expected: works if the cache is in shell_writable_paths; otherwise fails until you add it.
  • allow_network = false, then ask for curl https://example.com. Expected: fails.

11. Background processes

  • Ask the agent to start a dev server in the background, then /exit. Expected: warning listing it; k kills it; w waits; c cancels the exit.
  • Start one, then from another terminal kill -9 <blitz pid>. Expected: the server is gone within a second or two (lsof -i :<port> shows nothing).

12. MCP (real server)

Add to config:

[[mcp.servers]]
name = "fs"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "."]
prefix = "fs"
  • doctor --online shows mcp fs N tools. /mcp lists it.
  • Ask the agent to list files using the fs server. Expected: approval prompt naming fs__… and the server; the call works inside the sandbox.
  • Add agents = ["qa"]. Expected: the main agent no longer sees fs__ tools; invoke_agent → qa can use them.
  • kill -9 the CLI. Expected: no leftover npx / server processes.
  • During a session, pkill -f server-filesystem (kill the MCP server), then ask for another fs tool call. Expected: it works; ps shows a new server process.
  • Point an MCP server at a command that exits immediately (e.g. command = "false"). Expected: one “unavailable” warning, then one “paused for 15s” warning; later turns aren’t slowed and don’t repeat the warning.

13. Hooks

  • A pre_tool hook for run_shell_command that exits 2 with a message. Expected: shell calls are blocked with that message; other tools work.
  • A post_tool hook appending the JSON event to a file. Expected: one line per tool call, including tool name and args.
  • A post_tool hook sleep 3; cat >> /tmp/post.jsonl, then a turn with several tool calls. Expected: the turn is not slowed down; the events arrive in /tmp/post.jsonl a few seconds later, in tool-call order. Quit right after a turn: the pending events are still written (within about 5 s).
  • A prompt_submit hook that blocks prompts containing password. Expected: “Prompt blocked by hook”.

14. Web 💲 (search key)

  • web_fetch of a docs page. Expected: approval per host; readable text returned.
  • Ask it to fetch http://localhost:… or http://169.254.169.254/. Expected: refused (“not a public address”).
  • Configure web.search_provider = "brave" (or tavily) with a key; ask a question needing search. Expected: approval once per provider with s; results with titles/URLs; follow-up web_fetch works.

15. Forged tools (helios)

  • /agent helios, ask it to forge a small bash tool and run it. Expected: approval shows the code as a diff; run needs separate approval.
  • Restart, ask helios to list and run it. Expected: still there. Ask it to delete it. Expected: gone from ~/.blitz/uc_tools.

16. Scripting

  • git diff | blitz review this --output-format json | jq .result Expected: a single JSON object; jq works.
  • blitz --output-format stream-json "…" | jq -c .type Expected: session, then events, ending with result.
  • blitz --max-turns 1 "do a multi-step task"; echo $? Expected: exit code 3.
  • Without credentials: blitz "hi"; echo $? Expected: clear error, exit code 1.

17. Other platforms

  • Ubuntu 24.04 desktop: sudo apt install bubblewrap, blitz doctor. Expected: sandbox on, or a clear AppArmor reason (fix: an AppArmor profile for bwrap, or sysctl kernel.apparmor_restrict_unprivileged_userns=0).
  • Windows: run the .exe with Git Bash on PATH; a simple prompt and a shell command. Expected: works without the OS sandbox; doctor shows sandbox off with a reason.

18. Repository and release

  • git rm -r --cached go/bin and commit (bin is now ignored). Done: go/bin isn’t tracked.

  • Pin the actions in .github/workflows/go-*.yml to commit SHAs. Done 2026-09-25, at the latest release of each action’s current major: checkout v4.4.0, setup-go v5.6.0, cosign-installer v3.10.1, sbom-action v0.24.2, goreleaser-action v6.4.0. Newer majors exist (checkout v7, setup-go v7, cosign-installer v4, goreleaser-action v7); upgrade them separately and re-run a release.

  • Push; both go-ci jobs pass; the Linux job’s “Sandbox enforcement must not be skipped” step passes. Done 2026-09-26 (0c68c453, 4e008b41), after fixing the macOS gofmt check and the Linux parallel-cap test.

  • Tag v0.1.0 and push the tag. Expected: go-release creates a draft release with 5 archives, 5 SBOMs, checksums.txt, checksums.txt.sigstore.json. Done 2026-09-26: unsigned tag on 4e008b41, since GPG signing wasn’t available; the draft had all 12 assets.

  • Download the assets and verify:

    cosign verify-blob --bundle checksums.txt.sigstore.json \
      --certificate-identity-regexp '^https://github.com/rmcguinness/code_puppy/\.github/workflows/release\.yml@refs/tags/v' \
      --certificate-oidc-issuer https://token.actions.githubusercontent.com checksums.txt
    shasum -a 256 --ignore-missing -c checksums.txt
    

    Expected: Verified OK and every file OK. Then publish the draft. Verified 2026-09-26 with cosign v3.1.3 (the bundle was made by cosign v2 in CI), using the exact identity https://github.com/rmcguinness/code_puppy/.github/workflows/go-release.yml@refs/tags/v0.1.0: Verified OK. A tampered checksums.txt and a wrong identity were both rejected. All 10 files OK. The SBOMs are SPDX 2.3 with 96 Go modules (95 in the binary’s build info). The darwin/arm64 binary reports 0.1.0 and vcs.revision=4e008b41, and doctor runs. Draft assets need authentication: gh api repos/<owner>/<repo>/releases/<id>/assets, then download each asset with Accept: application/octet-stream. Not yet published.

  • Publish the draft. Published 2026-09-26 as Latest, after deleting a stray empty release on the same tag; the public checksums.txt is identical to the verified one. Binaries aren’t Apple-notarized: on macOS, a copy downloaded in a browser gets the quarantine flag, and Gatekeeper refuses to run it until it’s cleared (xattr -d com.apple.quarantine blitz) or opened through Finder’s context menu. Document this in the release notes, or notarize later.

  • After the move to retail-cortex/blitz (fresh history, 2026-09-26): the first v* tag pushed here produces a draft release, and it verifies against the new identity:

    cosign verify-blob --bundle checksums.txt.sigstore.json \
      --certificate-identity-regexp '^https://github.com/retail-cortex/blitz/\.github/workflows/release\.yml@refs/tags/v' \
      --certificate-oidc-issuer https://token.actions.githubusercontent.com checksums.txt
    shasum -a 256 --ignore-missing -c checksums.txt
    

    Expected: Verified OK and every file OK; the former repository’s identity is rejected. (The items above were run against rmcguinness/code_puppy.)

  • Desktop release, before the Apple secrets are set: a tag’s draft release has Blitz_<v>_macos_arm64.dmg, Blitz_<v>_macos_x64.dmg, the Windows zip, the .debs, Arch packages and tarballs for amd64 and arm64, and a .sigstore.json for each, and the macOS job warns that it signed ad hoc. cosign verify-blob --bundle <file>.sigstore.json (identity flags as for checksums.txt) accepts each.

  • Set the six secrets (spec_release_025 REL-22), tag again. Expected: the macOS job notarises (a few minutes) and spctl passes. Download the dmg in Safari, open it, drag Blitz to Applications, open it: no Gatekeeper warning, About shows the tag’s version, and “Install and start the service” works (the bundled CLI is signed too). On an Intel Mac too, if one is at hand.

  • On Ubuntu 24.04: sudo apt install ./blitz-desktop_<v>_amd64.deb; Blitz is in the app launcher with its icon, opens, installs the service (systemctl --user status blitz), and sudo apt remove blitz-desktop removes it.

19. Cost sanity 💲

  • After a day of use, compare /cost totals with your provider’s billing dashboard. Expected: same order of magnitude; if not, adjust [pricing].

20. Language

  • /locale Expected: “Interface language: English (US) (en-US)” and the list en-US, es, fr-CA.
  • /locale ES-sp Expected: confirmation in Spanish; ~/.blitz/.env.toml now has locale = "es" under [ui], with your other settings and comments unchanged.
  • /help, /cost, an approval prompt, and /exit with a background process running. Expected: all in Spanish; answer letters still y/s/a/n and k/w/c.
  • 💲 Ask a question. Expected: the answer is in Spanish; code, paths, and command output are unchanged.
  • Restart. Expected: still Spanish (banner, spinner “pensando”).
  • /locale ja 💲 Expected: a note that menus stay in English; replies come in Japanese.
  • Put a de.json with a few keys in ~/.blitz/locales/ (see docs/content/development/translating.md), then /locale de. Expected: those keys in German, the rest in English.
  • /locale en-XA Expected: accented ⟦…⟧ text everywhere; note any plain English you see.
  • Native-speaker review: someone fluent reads pkg/i18n/locales/es.json and fr-CA.json (tone, terminology, Québec typography for fr-CA) and runs a short session in each.
  • /locale en-US to switch back.

21. Images 💲

Use a real screenshot (e.g. a UI with visible text) saved in the workspace as shot.png.

  • Gemini: what does @shot.png show? Expected: a dim 📎 shot.png W×H, … KB line, then an answer that quotes text from the picture.
  • Anthropic: same. Then ask it to “look at shot.png with view_image”. Expected: a view_image badge and an answer about the picture (image delivered inside the tool result).
  • OpenAI (gpt-5 or another vision model): same two checks. Expected: no “unsupported content part” error.
  • Gemini: the view_image check. Expected: works; if Gemini rejects an image next to a tool result, note the error text here.
  • Ollama with a vision model (e.g. qwen2.5vl) and with a text-only model. Expected: vision works; text-only gives a clear API error, not a crash.
  • Take a screenshot to the clipboard (macOS: Cmd+Ctrl+Shift+4), /paste, then ask about it. Expected: 📎 clipboard-HHMMSS.png … will be sent with your next message. With text on the clipboard: “The clipboard has no image”.
  • Linux desktop: /paste with wl-paste (Wayland) or xclip (X11) installed.
  • A 4000×3000 photo: Expected: attaches as 1568×1176; the model still describes it.
  • @~/Desktop/x.png (outside the workspace) and @id_rsa_screenshot.png. Expected: refused; “Nothing was sent”.
  • Quit and --continue "what was in that image?". Expected: the model can still see it. Check ls -la ~/.blitz/images (files mode 600) and that the session file under ~/.blitz/sessions is small (no base64).
  • blitz --image shot.png "describe" --output-format json | jq .result Expected: a description.

22. Diagnostic log and telemetry

  • After any session: ls -la ~/.blitz/logs. Expected: directory mode 700, blitz-YYYY-MM-DD.jsonl mode 600 with a start line; a failed turn adds an ERROR line.
  • BLITZ_LOG_LEVEL=off blitz "hi". Expected: nothing new in the log.
  • Run a local collector, e.g. Jaeger: docker run --rm -p 16686:16686 -p 4318:4318 jaegertracing/jaeger:latest. Then BLITZ_TELEMETRY=1 blitz 💲 and do a turn that reads and edits a file. Expected: in Jaeger (http://localhost:16686, service blitz), one trace per prompt: turn → invoke_agent → generate_content … and execute_tool …, with approval under the edit’s tool span. No file content, prompt text or tool arguments in any attribute.
  • Two prompts, quit, then --continue with a third. In Jaeger search by tag gen_ai.conversation.id=<session id>. Expected: three turn traces with turn.index 1–3; turns 2 and 3 each show a link (“References”) to the previous turn, including across the restart.
  • Same with capture_content = true. Expected: prompts and tool arguments appear; your API key does not.
  • Stop the collector, then run and quit a session. Expected: exit is not delayed by more than about 3 s; no telemetry errors in the terminal (they go to the log at debug level).
  • blitz doctor. Expected: log and telemetry lines matching your settings.

23. Resilience 💲

  • Turn Wi-Fi off, send a prompt, turn it back on within about 5 s. Expected: the turn completes (retried); the log shows the failed attempts.
  • max_retries = 0, Wi-Fi off, send a prompt. Expected: a clear error; the session keeps working afterwards.
  • stall_timeout_seconds = 5, then ask for a long answer with provider = "openai" (not streamed). Expected: it fails after about 5 s with “model API stopped responding”. Restore the default afterwards.
  • Ask for a task that makes many tool calls at once (e.g. “read these 12 files”), with max_parallel = 2. Expected: it completes; with telemetry on, no more than two execute_tool spans overlap.

24. Steering 💲

  • Start the REPL. Expected: the hint line mentions typing (or Ctrl+T) to message the agent.
  • Ask for a multi-step task (read several files, then edit). While it works, type use tabs for indentation. Expected: output pauses and a ↪ message for the agent prompt appears with your text. Enter shows “Sent”, output resumes, and the agent’s next step reflects the message.
  • Press Ctrl+T during a turn. Expected: the same prompt, empty. Enter on an empty line shows “Nothing sent”.
  • Type a message while an approval prompt is showing. Expected: your keys go to the approval prompt, not a steer prompt.
  • Send a message just as the agent writes its final answer (no more tool calls). Expected: “The agent finished before reading your message; sending it now”, then a new turn with it.
  • During a steer prompt press Ctrl+C. Expected: the turn is cancelled (“Interrupted”), as Ctrl+C always does.
  • After a few steered turns, quit and --continue "what did I ask you mid-way?". Expected: it knows.
  • After quitting, type in the shell. Expected: echo and line editing work normally (the terminal mode was restored).
  • Linux desktop: the same basic check.

25. !, /plan, /tools

  • !git status and !ls. Expected: output as in your terminal, then ✅ Done (…); the agent’s next answer doesn’t know about it.
  • !vim README.md (or !less README.md), then quit it. Expected: the program works normally; the REPL prompt comes back intact.
  • !sleep 30, then Ctrl+C. Expected: ⚡ Interrupted; the session continues (no exit prompt).
  • !exit 3. Expected: ❌ Exit code 3. The audit log has a user_shell entry.
  • 💲 /plan add input validation to the signup handler. Expected: a “Plan mode” note; the agent reads files; any attempt to edit or run a command comes back as “plan mode: … disabled”; the answer is a numbered plan and no files change (git status clean).
  • 💲 blitz --plan "…" --output-format json | jq .result. Expected: a plan; no changes.
  • /tools. Expected: the active agent’s tools with ● on read-only ones; configured MCP servers listed for the primary agent only (unless agents says otherwise).

26. Model fallback 💲

Set fallback_models = ["anthropic/claude-sonnet-5"] with a working Anthropic key, and the primary on Gemini.

  • blitz doctor --online. Expected: model and fallback 1 each initialised and responding.
  • Break the primary: an invalid GEMINI_API_KEY. Ask something. Expected: one notice “gemini-… is unavailable; answering with fallback claude-sonnet-5”, then the answer. Ask again: no second notice and no delay from the primary. In the desktop app too: the notice shows in the conversation, not only in Settings › Logs.
  • /cost. Expected: priced at Claude’s rates.
  • Fix the key, wait 15 s or more, and ask. Expected: “gemini-… is answering again”.
  • fallback_models = ["anthropic/no-such-model"] with a broken primary. Expected: a clear “every model failed” error listing both reasons.
  • provider = "ollama" with no base_url and Ollama running locally. Expected: it works (it used to call api.openai.com).

27. Per-agent models 💲

  • /pin_model qa anthropic/claude-haiku-4-5. Expected: “qa now runs on claude-haiku-4-5”, “Saved in ~/.blitz/workspaces/-/.env.toml”; that file (not ~/.blitz/.env.toml) has [agent_models] with that line and your comments intact.
  • /agents. Expected: 📌 claude-haiku-4-5 next to qa.
  • Ask the main agent to have qa review a file. Expected: it works; /cost includes Haiku-priced tokens. With telemetry on, qa’s generate_content span names claude-haiku-4-5.
  • Restart. Expected: the pin is still there (/pin_model lists it). blitz doctor shows pin qa.
  • /model anthropic/claude-sonnet-5. Expected: the main agent switches provider.
  • /unpin qa. Expected: it runs on the configured model again; the line is gone from the workspace’s file.
  • Pin qa in workspace A, then open workspace B. Expected: in B, qa runs on the configured model (or B’s own pin); back in A, the pin is there.
  • Put qa = "openai/gpt-5" under [agent_models] in ~/.blitz/.env.toml, then /unpin qa in A. Expected: A’s file has qa = ""; A runs qa on the configured model, B on gpt-5.

28. Per-model settings 💲

  • /model_settings. Expected: “No model has settings of its own…”.
  • /model_settings gemini-3.8-flash temperature=0.1 seed=7. Expected: “gemini-3.8-flash now uses temperature=0.1 seed=7”, “Saved in …/.env.toml”; the file has [model_settings."gemini-3.8-flash"] with both lines and your comments intact.
  • Ask the same short question twice. Expected: it works; the answers are close to identical. With BLITZ_LOG_LEVEL=debug nothing is logged about dropped settings.
  • /model_settings gemini-3.8-flash. Expected: temperature 0.1, seed 7, max_tokens “(global: 65536)”, top_p “(provider default)”.
  • /model_settings anthropic/claude-sonnet-5 temperature=0.5. Expected: a warning that anthropic doesn’t accept temperature for claude-sonnet-5. /model anthropic/claude-sonnet-5 and ask something: it answers (no 400 error).
  • With an OpenAI key: /model_settings openai/gpt-5 seed=1, /model openai/gpt-5, ask something. Expected: a warning when setting; the answer works (the seed isn’t sent).
  • /model_settings gemini-3.8-flash temperature=5. Expected: “Nothing changed: temperature must be a number in [0, 2]”.
  • Restart. Expected: /model_settings still lists the settings. /model_settings gemini-3.8-flash reset removes them and the table from the file.

29. Session snapshots 💲

  • In a session, ask the agent to remember a word (“pineapple”). /session save fruit. Expected: “Saved snapshot fruit (2 messages)…”; /session list shows 📸 fruit.
  • Tell it “actually, remember mango”. /session load fruit, then ask “which word?”. Expected: “Started session … from snapshot fruit”; it answers pineapple and doesn’t know mango.
  • /resume <the original session's id> and ask again. Expected: mango.
  • /session save fruit. Expected: refused with a hint about --force; with --force it’s replaced and /session list shows one 📸 fruit.
  • Exit. blitz --continue "which word?". Expected: continues your last ordinary session, not the snapshot. blitz --resume=fruit "which word?": pineapple, in a new session.
  • In a session that used tools (e.g. edited a file), save, load, and ask what it just did. Expected: it knows about the tool calls, not only the chat text.

30. /search and Google search 💲

  • [web] search_provider = "google" with a Gemini key. blitz doctor --online. Expected: web search google: N results.
  • /search web golang errors.Is vs errors.As. Expected: up to five numbered links with real site URLs (no vertexaisearch.cloud.google.com links), “Handing these to the agent to read”, then an answer that cites some of them. No approval prompt for those pages.
  • In the same answer, if the agent tries a page that wasn’t listed. Expected: an approval prompt.
  • Ask it to save its findings to a file in that turn (e.g. /search web … and write notes.md). Expected: create_file is refused as read-only; no file.
  • /search session <something said earlier> after a /compact. Expected: “Found N messages…” and an answer that recalls the compacted detail.
  • /search session nonsense-word. Expected: “Nothing in this session’s transcript mentions it…”, and the agent says it never came up.
  • SearXNG instead: run docker run -p 8888:8080 searxng/searxng with json added to search.formats, set search_provider = "searxng" and search_url = "http://localhost:8888", then /search web …. Expected: it works, with no API key.
  • Remove search_provider. /search web x. Expected: “Web search isn’t set up…”.

Workspace search (spec_search_035)

  • In a real repository, blitz search --status right after opening it, then again a few seconds later. Expected: “still being indexed”, then counts per source and a last scan time. Note how long the first scan took for the repository’s size.
  • blitz search <a function name> and blitz search "<a phrase from a doc>". Expected: the defining file first, at the right line; the doc with its passage.
  • Put a secret in .env and search for it. Expected: nothing found. Add deny read(docs/**) and search for something in docs/. Expected: gone at once, before any rescan.
  • A PDF in the workspace: search for a phrase from its second page. Expected: a documents hit labelled “page 2”. A damaged PDF: --status counts it as unreadable, once.
  • Desktop: ⇧⌘F, type, toggle the chips, Enter. Expected: the file opens at its line; a chat hit opens the chat; light and dark both readable.
  • Ask the agent “where do we handle X?”. Expected: it uses search_workspace (visible in the tool calls), then reads the file.

31. Side questions (/btw) 💲

  • Ask the agent to remember a word. Then /btw what was the word?. Expected: “Side question: read-only…” and the word.
  • Ask “what did I just ask you on the side?”. Expected: it doesn’t know.
  • /btw create a file notes.txt. Expected: it says it can’t (read-only); no file.
  • /cost before and after a /btw. Expected: the side question’s tokens are included.
  • Exit and --continue. Expected: the recap and history have no trace of the side question.

32. Session names and resume hint

  • Start the REPL. Expected: the terminal tab/window title is “🐶 (untitled)”. Send a prompt; at the next prompt the title is its first line.
  • /rename Login work. Expected: “Session renamed to Login work.”; the window title follows; /session list shows it.
  • /exit. Expected: “Resume with: blitz –resume=session-…”; the terminal’s own title comes back. Running that command resumes the session.
  • ui.terminal_title = false. Expected: the window title isn’t touched.
  • In tmux, with set -g set-titles on. Expected: the tmux title follows the session name.

33. Skill definitions and [skills.policy]

  • Put a Castor-style SKILL.md (with scripts, tool_requirements, execution_hints) in ~/.blitz/skills/<name>/. /skills list. Expected: a line such as “1 script · TIER_2_AUDITED_WRITE · allowed by skills.policy”.
  • /skills show <name>. Expected: the content hash, required tools, tier, network, passed and withheld variables, and each script with ✓/✗ and its reasons.
  • Add custom_hints: {network: "true"}. Expected: the scripts are blocked (“needs the network…”). Set network = "allowlist" and network_allow = ["<name>"]: allowed.
  • Put the hash from /skills show in trusted_hashes, then edit the script. Expected: blocked (“isn’t in skills.policy.trusted_hashes”).
  • Break the frontmatter (hitl_tier: TIER_9). Expected: a startup warning naming the file; doctor shows it too.
  • [skills.policy] sandbox = "docker". Expected: doctor warns: use auto, gvisor or os.

34. Script sandbox (gVisor)

  • macOS: blitz doctor. Expected: script sandbox os (gVisor unavailable: gVisor runs only on Linux).
  • Linux without gVisor: doctor. Expected: script sandbox os (gVisor unavailable: runsc not found …).
  • Linux: unpack gVisor’s release tarball into ~/.blitz/bin (keep gvisor-bin/ beside runsc). doctor. Expected: script sandbox gvisor.
  • skills.policy.sandbox = "gvisor" on macOS. Expected: doctor warns that skill scripts won’t run.
  • Linux with gVisor: kill Blitz with kill -9 while a script runs, then start it again. Expected: runsc --root ~/.blitz/sandboxes/state list is empty after the restart.

35. Skill scripts and environments 💲

  • Put a skill with a Python script that has a dependency (e.g. requests>=2.31) in ~/.blitz/skills/<name>/. Ask the agent to use the skill. Expected: activate_skill lists the script as allowed; running it asks once to install (“Install packages for skill … –only-binary :all: – requests>=2.31”), then runs.
  • Ask again. Expected: no install prompt; /envs shows one environment with the package, “used by ”.
  • A script that writes $SKILL_OUTPUT/report.md and tries to write a workspace file. Expected: the workspace write fails, report.md appears under .blitz/skill-output/…, and the agent can read it and apply changes with the file tools (diff and approval as usual).
  • hitl_tier: TIER_3_MANDATORY_APPROVAL. Expected: asked before every run, with no “always” option.
  • A script that needs the network, without network_allow. Expected: refused with the reason; allowed after network = "allowlist" plus network_allow.
  • Linux with gVisor: a script that runs ls ~ and cat .env in the workspace. Expected: home doesn’t exist and .env reads as empty.
  • Change the requirements and run again. Expected: a new environment and a new install prompt. /envs prune removes the old one.

36. The service, attaching and workers

  • blitzd in one terminal, blitz in a workspace in another. Expected: the REPL says it attached; a turn streams as usual; an approval prompt appears in the REPL and its answer is honoured; /pin_model, /session save, /undo work.
  • While attached and idle, blitz --local -p hi in the same workspace. Expected: it runs in a private blitzd (the shared service hands the workspace over), which is gone when the run ends. While a turn runs in the shared service: refused, naming the other owner (exit code 2).
  • With no service running and no login item, blitz -p hi. Expected: blitzd starts, answers, and stops by itself 15 minutes later.
  • /cd: in a REPL with a few turns, /cd ../other-repo (one with .blitz/settings.toml needing trust). Expected: the trust question for the target, then “Now in …; the session moved with you”; /rewind offers no earlier prompts; /undo says the earlier changes are in the old workspace; the next answer knows it moved. blitz --continue in the target resumes the session; in the old workspace it doesn’t. With a background process running, /cd asks kill/wait/cancel, and c stays. /cd to a workspace another blitz --local holds: “Stayed in …”. Attached to the service, the same, and the old workspace stays open in the service.
  • 💲 Background tasks (REPL): “Ask the qa agent in the background to review README.md for broken links, then tell me a joke.” Expected: invoke_agent returns a task-1 at once and the joke comes without waiting; /tasks lists it running, then done; at the next prompt a dim line says it ended; the agent mentions its result in its next answer (or task_output). Ask for one that must run a command needing approval: at the prompt “qa · task-2 asks, from the background:” with the usual question; n refuses it. /tasks stop on a running one: stopped. /exit while one runs: the exit prompt lists it.
  • 💲 Background tasks (desktop app): the same prompts. Expected: a card under the tool group follows the task to done, Show gives its steps and result, Stop stops one; a request shows as a labelled card beside the turn’s own and Ctrl+J focuses it; after the task ends a notice appears, and with Settings › Continue after background tasks on (and nothing running) a new turn starts about it. Reopen the chat: the ended task is a notice.
  • Project settings: a scratch repository with .blitz/settings.toml holding [permissions] deny = ["web(*)"], a [[hooks.stop]] running ./stop.sh (which writes a file), [llm.openai] base_url = "https://example.invalid" and [blitz] auto_approve = true. Open it with blitz (and with the service running). Expected: a warning for each of base_url and auto_approve; the question lists the hook with ./stop.sh, and s prints the file. d: /permissions shows deny web(*) from project; a turn ends without the hook’s file. /trust, t (restart blitz when run locally): the hook runs after a turn. Edit stop.sh: asked again. Copy the repository elsewhere: asked again there. blitz exec in it: one line says the settings wait, and nothing of the hook runs; with --trust-project it runs. blitz doctor lists the project settings and the two ignored keys.
  • Project settings in the desktop app: open the same repository. Expected: the dialog lists the hook, what applies and what was ignored; Don’t trust leaves a bar above the conversation; Review reopens the dialog; Trust says they’re in force, and the bar goes. Settings › Workspaces › Project settings shows the same.
  • 💲 Attached: ask the agent to run sleep 300 in the background, then /exit. Expected: the exit prompt lists it; w waits (Ctrl+C gives up), k stops it (a second blitz attached to the workspace doesn’t list it, before or after). !ls, then look in ~/.blitz/audit/audit-<today>.jsonl: a user_shell entry for ls from the service.
  • Stop the service with Ctrl+C during a turn. Expected: the turn ends within about 10 s, the socket file is gone, and a new blitz in the workspace runs locally.
  • blitz service install on macOS, then log out and in. Expected: blitz service status says installed and answering; ~/.blitz/logs/service.log shows it started. With a key only in the shell, install warns about it. blitz service uninstall removes it.
  • Same on Linux with systemd (systemctl --user status blitz).
  • 💲 A worker workers/check/WORKER.md with schedule: every 5 minutes and permissions: ["write:reports/"] whose workflow writes reports/check.md and also tries to edit main.go. blitz workers enable check, wait. Expected: it runs on schedule; reports/check.md exists, main.go is unchanged; blitz workers runs check lists the run with one refusal; /resume <session> shows the conversation. Edit WORKER.md: the listing says “changed” and it stops running until re-enabled.
  • 💲 A worker with limits: {max_cost_usd: 0.001}. Expected: stopped as “limited” with the cost limit as the reason.
  • 💲 A worker with agent: qa and model: anthropic/claude-haiku-4-5 (the workspace on another model). blitz workers enable shows both; blitz workers run it. Expected: the run’s session (/resume it) is the qa agent’s, the run is priced as Haiku in workers runs, and the workspace’s /agents and /model are unchanged afterwards. With agent: nobody, blitz workers lists the problem and a run fails naming it.

37. Desktop app

  • bazel build //:mac-app, copy it out of Bazel’s output (ditto "$(bazel cquery --output=files //:mac-app)" /tmp/Blitz.app) and open it with no service running. Expected: it offers to install the service; accepting runs blitz service install and the app continues.
  • With the service running: open a workspace with “+”. Expected: a tab named after the directory, showing the active agent and model (or why the model is unavailable).
  • 💲 A turn in the app: its text appears as it streams, not only at the end. This checks that responses stream through the host (blitz-tray --serve) into the Electron window, which the tests can’t: they exercise the proxy without a window.
  • 💲 A turn that edits a file with approvals on. Expected: the approval shows the diff; “Allow once” edits the file; “Deny” doesn’t, and the agent says so.
  • 💲 While a turn runs, type a message and press Steer. Expected: “Queued for the agent”, and the agent takes it into account at its next tool call; Stop ends the turn with “Interrupted.”
  • 💲 An ask_user_question. Expected: the question with its options; the answer reaches the agent.
  • Two workspace tabs, a turn in each at once. Expected: both stream independently; switching between them doesn’t interrupt either, and the workspace dropdown shows a pulsing dot on each (a red ! when one waits for an approval).
  • First start: blitz service uninstall, move ~/.blitz/desktop.json aside, open the app, click “Install and start the service”. Expected: within a few seconds the welcome page (“Open a workspace”) replaces the install screen.
  • The window on macOS: the title bar area is the page’s (drag it by the top bar; the window buttons don’t overlap the workspace dropdown).
  • Settings › Appearance: System, Light, Dark. Expected: System follows System Settings › Appearance while the app is open; the choice survives a restart. Compact density tightens the lists and chat.
  • Edit a workspace’s name, description and colour (dropdown pencil, Settings › Workspaces). Expected: the dropdown and welcome cards change; after a restart the same workspaces are open, in order, with the same one shown.
  • Close a workspace (dropdown ✕): it moves to Recent, “Undo” reopens it. 💲 Close one while a turn runs: the dialog stops the turn first. With the REPL attached and running a turn in the same workspace, closing it in the app leaves the REPL’s turn running.
  • Stop the service (blitz service stop or kill it) with the app open, then click something. Expected: “isn’t answering. Reconnecting…”; start it again: “Reconnected” and the workspaces reload.
  • 💲 A model answer with a link, a table and a code block: the table and code render; Copy copies the code; clicking the link opens the default browser, not the window. An image in Markdown shows as a link and isn’t loaded.
  • Hover that answer: Copy, then paste into a text editor and a rich editor (e.g. LibreOffice). Expected: the text as shown, without the code block’s language label or buttons; the rich editor keeps the table and code formatting. Copy as Markdown pastes the Markdown source. Tab reaches the buttons.
  • A turn whose answer comes in parts (text, tool calls, more text). Expected: each part also has Copy the whole answer, which pastes every part’s Markdown in order.
  • 💲 Hover a prompt › Edit: the files and conversation go back, and the prompt is in the composer. “Code only” restores files and keeps the chat. After editing a file by hand, a rewind asks to overwrite.
  • 💲 /plan with a small task: the plan shows as a card; “Yes, carry it out” makes the change; typed feedback makes the agent revise. The status bar’s mode menu changes the permission mode (the REPL attached to the same workspace shows it too).
  • Run settings: change the model, effort, temperature (saved to [model_settings]), mode, agency; add and remove a permission rule; revoke an approval; compact the context.
  • Changes view after a turn that edited files: the files, their diffs, the agent’s summary, “Undo last turn”; the Git toggle shows every change since the last commit, staged or not, and new files. In the Files tree, Stage a modified file: its letter gets a tinted chip and the menu offers Unstage, not Stage; edit it again: the chip is outlined and both show.
  • 💲 Notifications: start a turn that needs an approval, switch to another app. Expected: macOS asks once to allow notifications; then “: approval needed”; clicking it brings Blitz back on that workspace. A long turn finishing while another workspace is shown notifies “done”; a quick one doesn’t; nothing notifies while you watch the conversation. Settings › Notifications off stops them.
  • 💲 Images: paste a screenshot (Cmd+Shift+Ctrl+4, then Cmd+V) into the composer, drop a PNG from Finder onto the chat, and attach one with the button: each shows a thumbnail with its size; ask about them and the model describes them. Dropping a file on the top bar does nothing (the window doesn’t open it).
  • History, with a worker run once: type part of a title (the list narrows, any case); Workers shows only ⏰ runs, Chats the rest; tick two and Delete 2: the dialog lists them, and after it one snackbar says “Deleted 2 chats”. The open chat has no checkbox to tick.
  • Commands: type / and pick with the arrows; /cost, /context, /checkpoints, /help show results; /nope is refused; /usr/bin is odd goes to the agent as text. 💲 /btw what is this repo? answers without adding to History’s message count; /search web connect-go interceptors lists links then reads them; a workspace command (.claude/commands/x.md) runs. Cmd+K: “changes” + Enter shows Changes; a workspace name switches to it; “theme dark” switches the theme.
  • Settings › Appearance › Interface language: Español, then Français (Canada). Expected: the whole window changes at once — top bar, dropdown, composer, menus, settings, the “Open a workspace” folder dialog’s title — and stays after a restart; System follows System Settings › Language & Region. Set "language": "en-XA" in ~/.blitz/desktop.json: no plain English left except what the service or model wrote.
  • A narrower window (about 1000 px): the run settings float over the chat, the Files shelf floats over the editor and minimizes when a file opens, the view switcher shows icons.
  • On Linux with a graphics card (EL-07): bazel run //:desktop with Use the graphics card (Settings › Appearance) on, then off, restarting between. Expected: the whole window drawn either way, not blank or garbled; idle, the app’s processes stay under 5% of a core; closing the window several times leaves no new dump in coredumpctl list. BLITZ_GPU=off turns it off too.

38. Parity features (ROADMAP item 25)

  • 💲 Ask “which test files are there?” in this repository. Expected: the agent calls glob with a pattern like **/*_test.go and lists them without shelling out to find.
  • 💲 blitz exec --max-cost-usd 0.01 "refactor the whole repository". Expected: it stops soon with “the turn reached its cost limit ($0.01)” and exit code 3 (echo $?); with --output-format json the result has is_error: true and exit_code: 3. The same with the service running (attached) gives the same exit code.
  • 💲 blitz exec --timeout 20s "run the full test suite and fix failures". Expected: stops after about 20 s with “time limit (20s)”, exit 3, and no test process left running.
  • blitz --max-cost-usd 1 with no prompt. Expected: usage error (exit 2). With a model that has no price, a one-shot run warns that the cost limit can’t be enforced.
  • A repository with only a CLAUDE.md (and one with a GEMINI.md). /memory. Expected: listed and followed; a CLAUDE.md that copies AGENTS.md is listed once.
  • AGENTS.md containing See @docs/style.md and @.env. Expected: /memory lists docs/style.md (imported by AGENTS.md) and not .env.
  • .blitz/rules/go.md with paths: ["**/*.go"] and a distinctive instruction. 💲 Ask the agent to edit a Go file. Expected: the first tool result on a .go file carries project_rules and the agent follows it; editing a Markdown file doesn’t trigger it.
  • 💲 /setup (or /init) in an empty folder. Expected: the agent asks what the project is for, its stack and commands, then writes .agents/AGENTS.md and AGENTS.md, CLAUDE.md, GEMINI.md, BLITZ.md as @.agents/AGENTS.md (approval with diffs), and offers skills and agents as multi-select questions. /memory then lists .agents/AGENTS.md once, imported.
  • 💲 /setup in a repository with a real CLAUDE.md. Expected: it reads the code first and asks only what it can’t tell; it asks before replacing CLAUDE.md with the import, after moving its content into .agents/AGENTS.md. blitz init -d <dir> runs unattended and lists its assumptions under “Open questions”.
  • 💲 /setup in an empty folder with no .agents/specs/. Expected: the first question offers “Write the specification first with /spec (recommended)”; choosing it ends the turn saying to run /spec, then /setup.
  • 💲 /spec a recipe-sharing web app in that folder. Expected: questions in rounds (intent and value, users, performance, platform, security: authentication and authorization, data, constraints, quality, delivery), then the planned list of specs to confirm, then .agents/specs/001-overview.md, the deliverables in build order with requirement IDs and acceptance criteria, and README.md. /setup afterwards reads them and its AGENTS.md has a Specifications section.
  • 💲 With skills or agents in place, change a spec. Expected: the desktop’s welcome shows “The specification changed: update the skills and agents”; /skills update offers additions, changes or retirements citing the specs, applies only the chosen ones, and suggests /agents update, which does the same for agents. In the terminal, /skills alone still lists the skills.
  • 💲 /spec in a folder with analysis.ipynb, data/survey.csv and refs.bib. Expected: the first question offers Research or science first; the rounds are the question, data, methods, compute, reproducibility, ethics, outputs and timeline (no deployment or sign-in questions); 001-overview.md has Kind: research, then literature, data, methods, experiments, analysis and write-up specs with Objectives. /setup afterwards proposes research skills (run and log an experiment, figures, citations) and agents (a literature reviewer, a statistics critic), and a .gitignore for raw data and results.
  • Desktop: a workspace with no instruction files. Expected: the first welcome tile is “Set up this workspace for agents”; it, the top bar’s wand and the command palette’s entry each start a new chat running /setup.
  • 💲 Desktop: after a few chats in a workspace, open a new chat. Expected: tiles for the latest chat (opens it), uncommitted changes and a failed worker, then “Learning from your recent chats…” and up to three ideas about that work; a new chat shortly after shows the same ideas without the hint (kept until a conversation changes).
  • /mode lists the five modes. /mode accept-edits, 💲 then ask for a file edit and a command. Expected: the prompt shows [accept-edits]; the edit happens without asking; the command still asks.
  • 💲 /mode plan, then “add a README section”. Expected: a plan, no file changed, and the transcript (/session list, resume) shows the prompt as typed.
  • blitz --permission-mode dont-ask exec "delete tmp.txt" (💲). Expected: the deletion is refused without a prompt; the agent reports it.
  • macOS or Linux with the sandbox: blitz --permission-mode bypass. Expected: the prompt shows a red [bypass]; commands run without asking. With [sandbox] shell = "off": --permission-mode bypass is a usage error, /mode bypass is refused, and auto_approve = true starts in default with a warning (doctor shows it under permissions).
  • [permissions] ask = ["shell(git push *)"], /mode bypass (with the sandbox), 💲 then ask the agent to push. Expected: it still asks; “always” doesn’t stop the next push from asking. deny = ["shell(rm -rf *)"]: rm -rf build is refused, also as bash -c 'rm -rf build'.
  • /permissions allow write(docs/**) then 💲 an edit in docs/: no prompt; an edit elsewhere still asks. /permissions remove write(docs/**) --save edits ~/.blitz/.env.toml without touching its comments.
  • deny = ["read(secrets/**)"], restart: read_file secrets/x is blocked, and a shell cat secrets/x fails inside the sandbox.
  • A worker with permissions: ["shell:git push *"] and a user ask rule for shell(git push *): the run’s push is refused and recorded.
  • .claude/commands/greet.md with Say hello to $1.; /help lists /greet under Custom commands; 💲 /greet Ada sends “Say hello to Ada.” and the transcript shows /greet Ada. Tab completes /gr.
  • 💲 /review on uncommitted changes: findings with file and line, and no file edited (plan mode). /verify runs this repository’s Bazel tests (bazel test //...) and reports.
  • A command with allowed-tools: Read, Grep: 💲 ask it to edit a file; the edit is refused with the allowed list.
  • 💲 /code-review focus on the new code runs the built-in skill by name.
  • 💲 [[hooks.stop]] command = "grep -q stop_hook_active || echo '{\"continue\": true, \"reason\": \"Now run the tests.\"}'": after a turn the agent runs the tests once, and the transcript shows (stop hook) Now run the tests..
  • [[hooks.session_start]] command = "echo 'Use tabs.'" and 💲 ask for a small edit: the agent follows it. [[hooks.notification]] command = "osascript -e 'display notification \"Blitz needs you\"'": a notification appears when an approval waits.
  • [[hooks.permission_request]] match = "run_shell_command" command = "grep -q '\"go test' && echo '{\"decision\":\"allow\"}'": 💲 go test runs without asking; other commands still ask.
  • 💲 /effort max with claude-opus-5-5, then a hard question: the answer arrives (no 400) and /set shows Effort: max; /effort auto clears it. --effort low on a one-shot with gpt-5 works too.
  • 💲 /model_settings claude-haiku-4-5 thinking_budget=4000 with temperature set globally: the call succeeds (temperature left out) and shows thinking in the transcript.
  • In a real terminal (Terminal.app, iTerm2, and over ssh): 💲 Esc while the agent works stops the turn (“interrupted”); arrow keys and Alt+arrows still edit the line; Esc at an approval cancels the turn; Esc in the steer prompt drops the message and the turn goes on.
  • Shift+Tab at the prompt shows [accept-edits], then [plan], then no tag, with the prompt redrawn in place (no leftover characters). Started with --permission-mode bypass (sandbox on), the cycle includes [bypass].
  • EDITOR=vim: type a draft, Ctrl+G, add lines, :wq — the whole text is sent and shown after the prompt; :cq or an empty file returns to the draft. VISUAL="code --wait" works. Esc Esc clears a typed line.
  • Pickers in a real terminal (and a narrow one, 40 columns): /agent, /model, /resume — arrows, typing to filter, Esc clears then cancels; the menu redraws in place and leaves one line. 💲 An edit approval: y/s/a/n answer at once, “Show the whole diff” shows it and asks again, Esc stops the turn. 💲 A question with options: arrows and Enter, “Another answer…” reads a line.
  • 💲 Have the agent edit a file, /exit, then blitz --resume=<id>: /diff shows the change and /undo reverts it. ls -l ~/.blitz/checkpoints/*/ shows owner-only files.
  • 💲 Three prompts, the second and third editing files. Esc Esc on an empty line: pick the second prompt → “Code and conversation”: the files are as before it, the model doesn’t remember prompts 2–3, and the second prompt is in the input line to edit. /rewind → “Conversation only” keeps files; “Summarize up to here” shrinks /context. Edit a file by hand, then rewind its prompt: the conflict picker offers to overwrite. /exit, --resume, /rewind still works.
  • 💲 /plan add a --version flag: the plan shows in a picker; “Yes, carry it out” makes the change in the same turn and .blitz/plans/ has the plan. Again with feedback typed (“also update the README”): the agent revises and asks again. /mode plan, a prompt, “Yes, and accept its file edits”: the prompt tag becomes [accept-edits].
  • 💲 A three-step task: the checklist appears and ticks off (☐ → ☒) as the agent works. plan_review = "always" in the config: a simple prompt is planned first.

39. Monorepo and Bazel (spec_monorepo_028)

  • A fresh clone on a Mac with only Bazelisk (brew install bazelisk): bazel test //... passes, downloading Go, Node and the tools itself.
  • The same on Ubuntu 24.04 with git and bubblewrap: bazel test //... passes; bazel build //:deb, sudo apt install ./…deb, and Blitz opens from the app launcher.
  • The Bazel-built Blitz.app (section 37) opens, shows its version under Settings › About (dev, or the tag in a --config=release build), installs the service (the blitzd it carries), and works as before. On an Intel Mac too, if one is at hand.
  • bazel run //:desktop-web, then http://localhost:5173/?fake: the page works without a service.
  • Your editor with GOPACKAGESDRIVER=<repo>/tools/gopackagesdriver.sh: go to definition from pkg/client into proto/blitz/v1 (generated) works; no false errors.
  • The first push of the branch: every CI step passes on both runners, and the reproducible job finds the archives identical.
  • The first tag after the move: the draft release has five archives with blitz, blitzd and blz, their SBOMs, checksums.txt and its bundle, the dmg and two .debs with bundles; blitz --version and blitzd --version show the tag.
  • Existing installs: after upgrading, a login item installed as blitz serve still starts the service (the hidden command runs blitzd); blitz service install rewrites it to blitzd.
  • With an older service running (or one whose program was deleted), bazel run //:desktop. Expected: a banner says the service is older (or its program is gone) with Restart the service; clicking it stops that service (its login item, else its process) and starts this build’s blitzd at login, and the banner goes, with “The service restarted”. Settings › Service shows both versions, the service program, and Restart and Stop: Stop stops it (the window then offers to start it), also for a blitzd started by hand in a terminal.
  • Open workspace from bazel run //:desktop: the folder dialog stays open until you choose.

40. API keys and per-workspace settings (spec_config_002 §6)

These touch your real keychain and settings: use a test key, or a throwaway --config DIR for the CLI steps.

  • blitz config set-key gemini, paste a key (not echoed). Expected: Keychain Access shows an item “Blitz: global/llm.gemini.api_key” (service dev.blitz); ~/.blitz/.env.toml has api_key = "keychain:global/llm.gemini.api_key"; blitz config keys says gemini keychain; blitz doctor and a turn work with no key in the environment. The first read may ask to allow security access: Always Allow.
  • A plain key in the file (api_key = "sk-…"): blitz config keys suggests secure-key; blitz config secure-key anthropic leaves only the reference in the file.
  • In a project: blitz config set-key -w openai and llm.provider = "openai" set for it (desktop, below). Expected: that project uses OpenAI with its key; another project still uses the global provider; nothing is written inside either project; the file is under ~/.blitz/workspaces/.
  • On Linux with GNOME Keyring: the same, with secret-tool search service dev.blitz listing the item. Without a Secret Service (a server): the key goes to ~/.blitz/secrets.toml, mode 600.
  • Desktop, Settings › Providers & keys: with no provider set, Gemini is shown; choosing a provider shows its status chip, matching blitz config keys; entering a key and Save stores it, and a workspace whose model was unavailable (“The model isn’t available…”) clears the note without reopening; Move to keychain on a plain key; Remove. The base URL saves on leaving the field.
  • Desktop, run settings › API keys: “Global” for inherited keys; a key saved there gives the workspace its own; a provider chosen there applies to that workspace only.
  • Desktop, Settings › </> (Show the settings file): typing [ lists tables, a new line in a table lists its settings, accepting one lists its values; the panel beside follows the cursor; Escape closes the completion list, not the dialog.
  • Desktop, Settings › </> (Show the settings file): the global file and a workspace’s; a typo’d setting saves with a warning; [llm is refused with the parse error and nothing is written; ⌘S saves; Revert restores.
  • Settings › </> (Show the settings file): TOML is highlighted. Type temprature = 0.3 under [blitz] and [llm on another line, then Validate. Expected: both listed with their lines (the typo a warning, [llm an error), both lines marked, the cursor on the error; clicking a problem goes to its line; Save refuses while the error is there. Hover provider under [llm]: its type, default and description. Every setting lists them all; searching “timeout” narrows it.
  • 💲 In the composer, type explain @ then part of a file’s name. Expected: a list of matching files and folders; Tab puts @path in; choosing a folder leaves the caret after its / and lists what’s in it. Send it. Expected: the answer uses the file without the agent calling read_file first; the prompt shows as typed. @shot.png also attaches the image.
  • Files view: right-click a folder, Add folder to context; open a file and click @ in its bar. Expected: @folder/ @file in the composer. Start a conversation about this file: a new chat with @file and the cursor in the prompt. A blocked file (.env, shown with a lock) offers neither.
  • Files view: open a README.md: it opens in Preview as a document; tables, task lists and code render; ⌘⇧V (Ctrl+Shift+V) switches to Source; edit and preview again: the edit shows. Go to file README.md:3 opens it in Source at line 3. In Preview, ⌘F opens the find bar: type a word, “1 of N”, Enter and ⇧Enter step through the marked matches, Escape clears them; in Source, ⌘F opens find and replace at the top. A workspace search hit in a Markdown file opens its Preview at the passage, the words marked.
  • In a preview, click ./other.md, ../README.md#install, #a-heading and src/main.go#L10: each opens the file (at the heading or line) or scrolls; a folder link shows it in the tree; ../../outside.md beyond the workspace is refused; a missing file says so. A ```mermaid block draws in the window’s colours (switch the theme: it redraws), Show source shows its text; a broken one shows its code and why. 💲 Ask the agent for an architecture overview in a .md: it uses Mermaid, not ASCII boxes.
  • Settings: the sections in order, under their headings, Getting started (Providers & keys, Appearance), Global setup (Agents, Skills, Workers, MCP servers, Secrets), Workspaces and access (Workspaces, Permissions), System (Service, Logs, About); in a narrow window the headings are thin rules between the icons (simple, no headings: Providers & keys, Appearance, Workspaces, About); the gear and ⌘, open on Providers & keys. Show advanced is in the title bar and nowhere in Appearance. With it on, </> turns Settings over to the settings file (a quick quarter turn, none with reduced motion) and back; type in the file, turn back and forth: the text is still there. Workspaces › a workspace › Edit the settings file turns over to that workspace’s file. Appearance: System/Light/Dark are equal widths, and the light theme, dark theme and language menus are one width. Service › Version shows only the service’s version (v0.7.x); with an older service running, the warning names both.
  • Settings › Skills: the registries first, then Built in listing code-review, git-workflow and testing-tdd with their descriptions, with or without a workspace open.
  • Settings › Appearance › Document styles…: in the window’s theme, uncomment --doc-accent and set a colour: the sample and open previews change as you type; the other theme’s CSS doesn’t apply until the window switches to it; Reset puts back the defaults.
  • Right-click a file in the tree: Show in Finder (macOS), Show in Dolphin / Files (Linux): the file manager opens with the file selected; on the tree’s background, the workspace folder. Open a .png (its pixel size under it), an .svg (Preview) and a multi-page PDF (every page, sharp; scroll). In the app, Open in the system viewer opens the image or PDF in the system’s viewer.
  • Studio › Workers › New worker: type what it does (the name follows), a schedule of whenever, a workflow; Create. Expected: the schedule’s problem in the dialog, nothing written. Fix it (Daily at 6 AM); Create. Expected: .agents/workers/<name>/WORKER.md exists with only the fields filled in; the worker is selected, New, and enables after review. A name already taken says so.
  • Tray (Ubuntu 24.04 and macOS): Settings › Service › Show in the system tray on. Expected: a filled bolt in the tray (top bar) at once, without logging out, white on a dark bar (and dark on a light macOS menu bar); its menu’s first line “Blitz service: running ()”. Stop the service: within seconds the bolt is an outline, the line says stopped, only Start is on; Start, then Restart: filled again each time, and the desktop app reconnects. Open Blitz raises or opens the app; Open the logs opens ~/.blitz/logs. Log out and in: the tray is back. Run blitz-tray again by hand: it exits (one at a time). Switch it off: no tray at the next login. On Ubuntu the bolt is amber (grey when stopped). An entry from an older install: write ~/.config/autostart/blitz-tray.desktop with Exec="/usr/lib/blitz-desktop/blitz-tray" (a path that’s gone) and open the app. Expected: the entry now names …/resources/app/bin/blitz-tray and the bolt appears without logging out.
  • Windows setup (Windows 11, a user who isn’t an administrator): run Blitz_<version>_windows_x64_setup.exe. Expected: SmartScreen’s More info › Run anyway, then the setup’s question and no administrator prompt; Blitz opens; %LOCALAPPDATA%\Programs\Blitz has Blitz.exe and Uninstall Blitz.exe; Blitz is in the Start menu and on the desktop; Settings › Apps › Installed apps lists Blitz with its version and Retail Cortex. Run the setup again with Blitz open: it closes Blitz and replaces it. After a self-update the installed apps list shows the new version. Uninstall it there: it asks, Blitz closes, the folder, shortcuts, entry and the tray’s start at login go, blitz is off PATH in a new terminal, and %USERPROFILE%\.blitz is still there. setup.exe /S installs without a question or starting Blitz.
  • Studio (advanced): the menu bar shows Workspaces, Changes, Studio, Help; Studio is a button (no menu) that opens the Studio drawer, its kinds down the left, with Reload agent files in its title bar; a failed worker puts a red dot on the button, gone once Studio › Workers has been shown. Agents: the workspace’s agents fill the pane; click one and its form slides in over the list; Cancel (asks first with unsaved edits), the back arrow and Save slide back to the list, and Save and Cancel stay at the bottom while the form scrolls; Redraft offers a draft with what changed, Use puts it in the form unsaved. Secrets: the workspace’s own, shown as {secrets:workspace:key} with urn:blitz:<workspace>:secrets:key; the workspace’s settings no longer have a Secrets card.
  • Workers in Studio: Studio › Workers lists the runs going now (each with Stop), then the workers. Click one: its view slides in (Run now, Disable or Enable, Edit, Delete); Edit slides to the form, back returns to the view, back again to the list; with unsaved edits, back and ✕ ask first. The status bar’s failed-runs button opens Studio on the failed worker.
  • Global workers: Settings › Workers › New worker in the Global workers box; Expected: ~/.blitz/workers/<name>/WORKER.md, and ~/.blitz/home exists. Enable it and Run now: it runs in ~/.blitz/home. The tray lists it as Global › <name>, and its Show in Blitz opens Settings › Workers.
  • Worker limits: Settings › Workers › Limits for every worker: set Model calls’ default to 7 and leave the field: Expected: “Saved the worker limits”; the global settings file has default_max_turns = 7 under [workers.policy], and a worker without max_turns shows 7 model calls. Type lots in Cost: the reason in a snackbar, the field back as it was. Turn every kind off: “workers may only read”, allow = []. Set Runs at once to 3: the form says the service runs 2 until it restarts; Settings › Service › Restart, and the note goes.
  • Every workspace’s workers: enable a worker in a workspace, remove the workspace from the app (Workspaces › Forget): Settings › Workers lists it Not in your workspaces, and Add and open brings it back on its workers. Rename that folder: within a minute it’s Folder missing (the service log says so once); Forget asks, then it’s gone from the list and from ~/.blitz/workers.json; rename the folder back: its worker is New.
  • Skills in Studio: Studio › Skills › +: type what it’s for (the name follows), instructions; Save. Expected: .agents/skills/<name>/SKILL.md, listed as Simple. Open it: References › New file style.md, type, Save file (⌘S too): the list says With references; Upload… a PDF and a PNG and open each: the PDF’s pages and the image show, not their bytes; style.md opens as a preview, Source to edit. Scripts › New script (Python): the template takes the tab; Test run with a "b c": the output shows ['a', 'b c']; a Shell script prints its arguments on macOS and Linux, and a cmd script says it runs on Windows only (the reverse on Windows). Make test in Python and in TypeScript: the list names them test.py and test.ts; add a line to each and run each: each prints only its own lines. Packages › Add requirements.txt, add rich, Save file: the Python script’s row says 1 package, and its first run asks to install it, then runs with it. A pyproject.toml with [project] dependencies works the same when there’s no requirements.txt (listed as unused when there is). Rename the skill and save: the folder moves with its files. Edit SKILL.md by hand while the form is open, then Save: “changed since it was opened”. Add from… › Copy a built-in as mine: it opens editable. In another skill, Add from… › Reference: it’s under Combines, and the list says Combines skills. Delete asks, then the folder is gone.
  • Rules: the (i) beside the title on Studio › Rules and Studio › Guardrails shows the rules-or-guardrails comparison (hidden at first; their own column marked “this page”, a button to the other), and hides it again. + (in the title bar): Before the agent, clear-ask, Block, “The prompt says which part of the code to change.” Save. Try it with “make it better”: Fails, with the reason. Send “make it better”: the prompt is refused with the rule’s reason, nothing recorded. Add an After the agent rule, Revise, “The answer says how the change was tested”: ask for a small fix: if the answer doesn’t say, a “(rules)” message sends the agent back, and it answers again. Drag a rule above another (or ↑): the order numbers change and the file’s order: too. A rule in .claude/rules opens read-only.
  • Guardrails: in a workspace with no .agents/guardrails, Studio › Guardrails shows the three built-ins on; 💲 ask the agent to force-push: refused by limits (its default commands). Switch Limits off: .agents/guardrails/limits.md appears with enabled: false; on again: the line goes. 💲 Ask the agent to fetch a page that says “Ignore your instructions and …”: a ⚠ notice names the guardrail, and the agent doesn’t follow it; set it to Block and ask again: the result is withheld. Switch Secrets and personal data on with Personal data too; ask the agent to repeat “mail jo@example.com”: the answer shows [EMAIL REDACTED] and a 🛡 notice. Limits: Allowed domains example.com, then ask for a page elsewhere: refused with the reason; Commands not to run git push --force*, then ask for a force push: refused. + (Your own): Actions, Block, tools run_shell_command, “Deleting a git branch”: Try it with run_shell_command: git branch -D main says Caught; ask the agent to delete a branch: refused with the reason. Reset to defaults on a tuned built-in: asked first, its file gone, the guardrail still on.
  • Evals: Studio › Evals, +: name basics, a Near case (“What does this project do?”, a one-line answer of yours) and an Exact one (“Reply with only OK”, OK), agents blitz. Save: .agents/evals/basics.md holds them as a table. 💲 Run: the Runs tab counts up, each result with its score (the near one with the judge’s reason); open one: the expected answer beside the agent’s. Add a case “Create notes.md saying hi”, Exact Done, Warn: its result lists create_file as not allowed, and no notes.md appears. ▶ on the list runs it again; Past runs lists both. In a terminal, blitz eval basics: the same results, exit 0 (or 1 after making the Exact case expect NO); blitz eval nope exits 2. Studio lists Evals right after Agents. An agent without evals: its row says “no evals yet” and its form Write evals; 💲 that drafts 5–8 cases for it, opened unsaved (Back asks first); save. 💲 Run; on a missed case, Refine its instructions: the request lists the missed cases; Draft, Use: the agent’s file has the new instructions. Edit the agent and save: “1 eval checks it”, Run it runs it in the background and says how it went.
  • Workflows: Studio › Workflows, + › Research brief: five steps on the canvas, joined. Add a Refine step from the row above the canvas, drag a link into it from Draft and out of it to Save, delete the old Draft → Save link (select it, Delete). Linking Save back to Notes isn’t allowed (a loop). Set the Notes path to a real file; Save: .agents/workflows/research-brief.flow.yaml. 💲 Run with “coupon rounding”: each step is marked as it runs; Save to a file asks for approval beside the canvas; allow it: the file is written and the run finishes; select a step to see its output. The run’s chat is among the background runs. 💲 In the chat, /research-brief coupon rounding: a line per step, each agent step’s answer in the chat, the save approved as usual. Name a block rule on Draft that fails: the workflow stops there with the rule’s reason. blitz in a terminal: /research-brief x prints ▸ /research-brief › … per step.
  • Reviewing scripts: add a script to a trusted workspace’s skill in Studio: the skill list says Scripts to review and its view has a banner; ask the agent to use the script: it asks first (Running… with a spinner while it runs). Review scripts › Trust these scripts: the banner goes and the next run isn’t asked about; edit the script: the banner is back. Install a registry skill with scripts in Settings › Skills: the review opens; Not now, then the installed list’s Review scripts opens it again.
  • \skill in the chat: type \rel: the composer offers the skills; Tab puts in \release-notes . Send it: the model answers using the skill’s instructions; the transcript shows the prompt as typed.
  • Secrets completion: type {secrets: in the composer, an agent’s instructions, an MCP server’s environment, the settings file (</>) and a file in the Files editor. Expected: global: and workspace: first, then the names; Tab puts in {secrets:global:name}; Escape closes the list without closing the drawer; a name that doesn’t exist is warned of under the instructions.
  • Secrets: Settings › Secrets › add demo_token = demo-value-123456; a workspace’s settings › Secrets › add demo_token = workspace-value-1234. In that workspace, 💲 ask Repeat this exactly: {secrets:demo_token}. Expected: the conversation and the history show {secrets:demo_token} as typed; the answer shows [REDACTED]; ~/.blitz/logs has no workspace-value-1234; Keychain Access (or Credential Manager) has urn:blitz:…:secrets:demo_token. Replace and delete one: the list follows.
  • Tray submenus (macOS, Ubuntu, Windows), with two MCP servers and a workspace with workers: MCP servers lists both, checked; uncheck one: Settings › MCP servers shows it Off within 5 s, and a turn no longer offers its tools; check it again. Workers lists each worker (with its folder when two workspaces have workers); Run now starts a run, which appears first as Stop … (running); clicking that stops it. Uncheck Enabled and check it again: the worker is disabled, then enabled, without a review. A new worker says Review it in Blitz to enable it. Show in Blitz opens the app on that workspace’s workers, even with the app closed. Stop the service: both submenus go.
  • Settings › MCP servers (with or without a workspace open; it’s global): Add server with a command (npx -y @modelcontextprotocol/server-everything): it’s listed, on, and the next turn in an open workspace has its tools without restarting the service. Edit it; turn it off: the next turn has none of its tools. Remove asks first. A bad name, address or environment line is listed under the form and nothing is saved.
  • Command line (Ubuntu 24.04 and macOS), with no blitz on PATH: Settings › Service › Command line says so; Install. Expected: a link in ~/.local/bin (or a folder already on PATH), and if ~/.local/bin wasn’t on PATH, a line in ~/.zshrc (~/.bash_profile for bash on macOS, ~/.bashrc on Linux); a new terminal runs blitz --version, and the row now says this app’s is on PATH. With Homebrew’s blitz on PATH instead, the row names it and offers no Install.
  • The sandbox required on Linux: on Ubuntu 24.04 with the AppArmor restriction on and no [sandbox] shell in the settings, blitz and the desktop app refuse to open a workspace, saying why and how to fix it or set auto, and blitz doctor fails its sandbox check; after sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 they open, sandboxed (/sandbox); with shell = "auto" they open, unsandboxed, and doctor warns.
  • Fixing it: on Ubuntu 24.04 with the restriction on, blitz security says AppArmor stops bwrap; blitz security fix-apparmor asks for the sudo password, writes /etc/apparmor.d/blitz-bwrap and loads it, and then blitz security, blitz doctor and opening a workspace all work, sandboxed, while unshare -Ur true (another program) is still refused. In the desktop app, opening a workspace shows a notice above it, This workspace didn’t open…, with Allow bubblewrap: the system’s password dialog, then Restarting the Blitz service…, and the workspace opens, sandboxed. Settings › Service shows OS sandbox with the same button and restart; cancelling the dialog shows the error and the commands to run by hand. On macOS the row isn’t there.
  • ADC shared only on request: signed in with ADC, a command (env | grep GOOGLE_APPLICATION_CREDENTIALS) finds nothing; with [sandbox] share_adc = true it names a copy that gcloud auth application-default print-access-token can use, gone after the command. The same key in a project’s .blitz/settings.toml is listed as ignored. cat ~/.config/anthropic/configs/* from a command is refused unless Claude signs in with ant.
  • Changes › Version control in a folder that isn’t a repository. Expected: “This folder isn’t under version control” and Turn on version control, no error; clicking it runs git init (a .git folder), the view shows no changes and the status bar the branch within a second. With git off the service’s PATH: it says git isn’t installed, with no button.
  • Tray language: Settings › Appearance › Interface language Français (Canada), then quit the tray and start it again (switch it off and on). Expected: the menu is in French (“Service Blitz : en cours”, Arrêter le service…). Back to System: the system’s language, English when it has no catalog.
  • 💲 Usage after a restart: two prompts, note /cost and /context; quit and blitz --continue (or restart the service and reload the desktop app). Expected: the same totals and context size, not zeros; another prompt adds to them.
  • 💲 Chats survive a service restart: start a chat in the desktop app, restart the service (Settings › Service › Restart), send two more prompts in the same chat, then reopen it from the history. Expected: every prompt and answer is there, titled by the first prompt; no “Failed to save session” in Settings › Logs.
  • Settings › Logs: today’s service log, newest first. Errors leaves only errors; searching a workspace’s name narrows to its records; another day from the list shows that day; Open the folder opens ~/.blitz/logs in the file manager. With log.level = "off" (and the service restarted) it says the log is off.

41. Files: the shelf and the editor (spec_files_029, phase 1)

  • In a git project, the top bar’s Files button: the tree lists folders first, modified files in orange with M, new ones in green with U/A, folders with changes marked. Dotfiles, ignored files (node_modules, bin/) and .env aren’t listed; Show hidden files lists them dimmed, .env with a lock (“Hidden from Blitz”); the setting is remembered after a restart.
  • Open a Go, a TypeScript, a Markdown and a YAML file: each is highlighted in the window’s colours (light and dark); typing offers the file’s words and the language’s keywords; ⌘F searches and replaces; ⌃G goes to a line.
  • Change a file, ⌘S: the tab’s dot goes, the tree shows it modified, git diff shows the change. Ask the agent something next: it’s told you edited the file (and your prompt in the history doesn’t show that note).
  • Edit a file in another editor while it’s open and unchanged here: within 5 seconds it reloads. Change it here too, then save: “This file changed since you opened it” — Overwrite writes yours, Reload takes theirs.
  • Let the agent edit a file you have open (unchanged here): it reloads after the tool runs. With your own unsaved changes: “Changed on disk…”.
  • Right-click in the tree: New file (opens it), New folder, Rename (F2), Delete (asks), Copy path, Copy relative path. Arrow keys move and open folders; Enter opens.
  • ⌘P: type part of a name (dsc finds discount.go); name:12 opens at line 12.
  • A path in an answer (internal/cart/discount.go) and a tool call’s path open the file.
  • Unsaved changes: closing the tab, closing the workspace (dropdown) and quitting the app (⌘Q, the window’s close button) each ask first.
  • With no file open, the chat is center stage (the middle, full width of its column); open a file during a turn: the chat moves to the right and keeps streaming; close the last file: it returns to the middle. In Changes or Workers it stays on the right.
  • The IDE layout: files left, editor middle, chat right. Minimize the shelf (≪): a rail with Show files and Go to file remains, and stays after a restart. Drag the chat’s left edge: its width is kept after a restart.
  • The workspace dropdown: switch workspaces; while a turn in another workspace waits for approval, the dropdown shows a !; reopen a recent workspace; Open workspace…; Settings is the top bar’s far-right button.

42. Licensing (spec_release_readiness_030 §3)

  • blitz license: Blitz’s NOTICE (Copyright 2026 Retail Cortex, then Code Puppy’s MIT notice) and where the rest is; blitz license full the Apache License; blitz license third-party the notices, in less (or $PAGER); /license in the REPL the same.
  • blitzd --license, --license=full, --license=third-party: the texts, and the service doesn’t start.
  • The desktop app: Settings › About › Licenses and Third-party notices, and /license in the composer, open the Licenses dialog; its three tabs show the texts.
  • A release: each archive has LICENSE, NOTICE and THIRD_PARTY_NOTICES; Blitz.app/Contents/Resources has them; after installing the .deb, /usr/share/doc/blitz-desktop/ has copyright, NOTICE and THIRD_PARTY_NOTICES.
  • Add a Go or npm dependency without updating the notices: CI’s “third_party_notices –check” fails until tools/third_party_notices.sh is run.

43. The docs site (spec_release_readiness_030 §6)

  • bazel run //docs:serve, then http://localhost:1313: the menu has Getting started, Products, Guide, Architecture, Shared packages, Development and About; search finds a word from a spec; the dark-mode toggle works.
  • After a push to main that changes docs/, the docs workflow deploys, and https://retail-cortex.github.io/blitz/ shows the change; the architecture page’s diagrams render; Edit page opens the file on GitHub.
  • A link to a missing page ([x](nope.md) in any page) fails bazel build //docs:site with the page and the link named.
  • On a phone-width window, the menu opens from the ☰ button and no page scrolls sideways.
  • Architecture › API reference: a page per proto; every diagram draws (the workspace page has 100 and takes a few seconds), in light and dark mode; a comment with a placeholder (turn.proto’s plan field: “/plan ”) shows the placeholder.

44. README, owners and coverage (spec_release_readiness_030 §7)

  • On GitHub: the README shows the desktop screenshot and its links work; Contributing in the repository’s sidebar opens docs/CONTRIBUTING.md.
  • Open a pull request: GitHub requests a review from the owner in .github/CODEOWNERS.
  • CI’s Linux job summary has the coverage table; lower tools/coverage/floor.txt’s number above the total on a branch and the job fails.
  • After CI on main, the site’s About › Coverage shows the same total, the commit it measured, and each package’s files when expanded.

45. Windows: the tray and its page (spec_windows_036)

On Windows 10 (1803 or later) or 11: unzip blitz_<version>_windows_amd64.zip (or bazel build //release:archives), with no API key set for a free run.

  • Run blitz-tray.exe. Expected: no console window; an amber bolt in the notification area within a few seconds (the tray started blitzd.exe); its tooltip says “Blitz service: running (…)”. A second blitz-tray.exe exits at once.
  • Open Blitz. Expected: an Edge window with no tabs or address bar shows the desktop page, connected; the address it lands on is http://127.0.0.1:<port>/, not the /open?t=… link. Open a workspace (type its path) and run a turn: it streams, and an approval works.
  • In that window: Show in File Explorer on a file, Settings › Service’s controls, Help › About’s licenses. Expected: Explorer opens with the file selected; Stop turns the icon grey and Restart amber; the license texts show.
  • Stop the service, then Start the service from the tray. Expected: the icon goes grey, then amber; ~/.blitz/logs shows the service stopping cleanly (“exit: a client asked”).
  • Tick Start Blitz at login, sign out and in. Expected: HKCU\Software\Microsoft\Windows\CurrentVersion\Run has Blitz; after signing in, the icon is back and the service runs. Untick it: the value is gone.
  • From PowerShell, curl.exe -i http://127.0.0.1:<port>/ (the port from the Edge window), and the same with -H "Host: example.com". Expected: 403 both times: no cookie, and a foreign host.
  • icacls %USERPROFILE%\.blitz\run Expected: only you, SYSTEM and Administrators.

46. Word documents (spec_filetools_006 FS-75, spec_desktop_024 DSK-93b)

  • In the desktop app, open a Markdown file with headings, a numbered list starting at 5, a task list, a table, code, a link to #a-heading and a workspace image; Export › Export as Word. Expected: <name>.docx beside it; a second export asks Replace .docx?.
  • Open it in Word, then in Pages (or LibreOffice). Expected: no repair prompt; headings in the navigation pane; the list counts 5, 6; the table’s header row, alignment and borders; code in a monospace shaded block; the heading link jumps; the image fits the page.
  • In a session, /export chat.docx; from a shell, blitz sessions export <id> -o chat.docx. Expected: both open in Word with the conversation’s headings and code.

47. Branches on the Changes page (spec_desktop_024 DSK-90c, spec_service_021 SVC-37)

  • In a repository with a remote and a second branch on it, open Changes › Version control and click the branch button. Expected: Branches lists the local ones (the current one checked), On the remote the remote’s branches no local branch tracks, each with its last commit’s subject.
  • Choose a remote branch. Expected: “Switched to .”; the status bar shows the new branch, the toolbar’s Pull has its upstream, and files open in the editor show the branch’s content.
  • Edit a file the other branch also changed, without committing, and switch to that branch. Expected: “Your uncommitted changes would be overwritten by that branch: commit them first.”; still on the same branch, the edit kept.
  • New branch…: try a b, then an existing name, then try/branches. Expected: the first two are refused under the field; the third switches to the new branch with your uncommitted changes still there.
  • Commit on the new branch, switch back to main, and Merge into main… with try/branches. Expected: “Brought 1 commit from try/branches into main.”; merging it again says main already has everything in it.
  • Make both branches change the same line, then merge. Expected: “Merging try/branches stopped on conflicts…”; the banner says Merging try/branches (not the upstream); Take theirs takes the merged branch’s line; Complete merge commits “Merge branch ’try/branches’”. Repeat and Abort merge: the dialog says the branch goes back to before the merge, and it does.
  • Click the branch in the status bar. Expected: Changes opens on Git (in simple mode, on this session’s changes).

48. Plain words for version control (spec_desktop_024 DSK-90d)

  • In each language (Settings › Appearance › Interface language), go through the Changes menu, the Changes view’s toolbar, the commit dialog, a file’s right-click menu in the Files tree, its history and the status bar’s branch. Expected: no “Git”, “stage” or “.gitignore” in a label: Version control, Commit changes…, Include in commit / Leave out of commit, Include all changes, Do not version / Version it again, View history…. Git is named only when it isn’t installed, in the sign-in message and in a repository-config warning.

49. Agent files apply from the next message (spec_memory_012 MEM-25, spec_desktop_024 DSK-91d)

  • 💲 In a workspace with a rule in .agents/rules/ (say, “Answer in French”), start a conversation and send a message; then edit the rule (“Answer in German”) in an editor and send another, in the same conversation. Expected: the second answer follows the edited rule, with no reload. The same for AGENTS.md, and for a skill’s SKILL.md edited in place (its new description in /skills).
  • 💲 Send several messages without changing anything. Expected: nothing reloads (the turns aren’t slower, and the provider’s prompt cache keeps hitting: /cost shows cached tokens).
  • Agents › Reload agent files, then /reload in the chat. Expected: “Reloaded N agents, N skills and N instruction files and rules.”; /reload lists the files. Break a skill’s frontmatter and reload: the error says which file didn’t load.

50. Panels stay put: the drawer (spec_desktop_024 DSK-65c, DSK-60)

  • Open Settings (⌘,), then drag the window by the title bar. Expected: Settings slides down from under the top menu over the whole middle, to the status bar; the window moves and Settings stays open.
  • Open, in turn, Help › User guide, Changes, the workspace’s history, an agent (Studio › Agents), a worker, the background runs (status bar) and Settings › About › Licenses. Expected: each slides down in the drawer, taking the place of the one before; the pinned chat input steps aside while it’s up and comes back when it closes; ✕ or Escape (with the focus in it) closes it.
  • Studio › Workers › New worker (and Studio › Agents › +), type a few words, then press ⌘, (or ✕, or Escape in the form). Expected: “Discard changes?”; Cancel brings the form back with your words, Discard closes it.
  • Open Commit changes… (or delete a chat) and click beside the dialog, then drag the window by the title bar over the dimmed area. Expected: the dialog stays open and the window moves; Escape or its buttons close it.
  • Light and dark, with Transparency on and off. Expected: the drawer is a pane like the chat’s, the panes under it not showing through, with no clipped controls; in a narrow window Settings’ sections are a rail of icons.

51. Workflow tasks: people and time (spec_workflows_043 WF-16–WF-18, spec_electron_038 EL-110)

Use a test service with the API keys unset (env -u GEMINI_API_KEY); the workflow below needs no model.

  • Save .agents/workflows/approve.flow.yaml with a read_file step, an Ask a person step (the starter form) and a save_file step after it; Studio › Workflows › approve › Run. Expected: the first step finishes, the second is marked waiting, and its form shows under the run; Approve sends it, the run goes on and asks to save.
  • Run it again and close the app window (leave the tray running). Within 10 s: Expected: the tray’s icon has a dot and its menu says 1 workflow task waiting; a notification “approve is waiting for you” shows. Clicking it opens the small tasks window (only the form, Later, Open Blitz); Approve sends it and the window closes; the dot goes.
  • Settings › Appearance › Workflow tasks: Show the task at once, quit the app, run the workflow from the CLI (blitz then /approve) and exit the CLI. Expected: the tasks window opens by itself, without a notification. With Off, nothing opens, but the tray’s dot and menu item still show; the item opens the window.
  • With the app open on another workspace, start a run that waits. Expected: a snackbar “approve is waiting for you: …” with Open (or a system notification while the window is behind another); the status bar shows the task button, which opens the tasks dialog. Later closes it and the button stays.
  • A Wait step with wait: 1m between two steps. Expected: the run waits; about a minute later (the service’s scheduler) it goes on by itself, also after a service restart in between.
  • Edit the workflow while a run waits, then answer its task. Expected: “the workflow changed since the run paused”: the run isn’t resumed.
  • Linux without a notification daemon, and Windows. Expected: Linux opens the tasks window directly; on Windows the notification comes from the Blitz app and opens the same window.

52. Workflow runs: the canvas, receipts, schedules and modes (spec_workflows_043 WF-09, WF-10, WF-19, WF-20, WF-21)

Use a test service with the API keys unset (env -u GEMINI_API_KEY) for everything but the cost checks; a run with agent steps on a real model costs a few cents.

  • Studio › Workflows › a workflow. Expected: Studio’s kinds shrink to icons and the canvas fills the drawer. Steps opens the toolbox (steps under it move into view); drag Agent onto the canvas: it lands where it’s dropped and its settings open. Click a step type instead: it lands in view.
  • Drag the settings’ left edge, then fold them with ». Expected: the canvas widens; the rail names what’s selected and unfolds them; selecting a step unfolds them too. The width is kept after reopening Studio; double-clicking the edge restores it.
  • An agent step’s Skills and Rules. Expected: a drop-down of check boxes (with a filter when there are more than eight); ticking keeps it open; the field lists what’s chosen.
  • Run a workflow with an agent step and a save_file step, then close Studio at once. Expected: the status bar shows /name 1/3-style progress; its menu shows steps, time, cost and 1 approval waiting when it asks; clicking it reopens the canvas on the run, with the approval under the canvas.
  • On the run panel: Steps, Events, Stream, Copy the receipt, fold, drag its top edge, close. Expected: each step’s start, duration, model, tokens and cost (agent steps priced, others “—”), the run’s totals; events as they happen; the agent step’s text appearing as it’s written; the copied text is the receipt; closing the panel doesn’t stop the run, and reopening the workflow shows it again.
  • Deny the approval with Studio open. Then run a workflow whose read_file step reads a missing file, and close Studio before it fails. Expected: a failure shown on the canvas isn’t announced again; with Studio closed, a snackbar “/name failed. It stopped at …” with Open, and a red item on the status bar until the run is opened; with the window behind another app, a system notification.
  • Close the app window (leave the tray), and let a scheduled run fail (a read_file step on a missing file). Expected: within 10 s a notification “ failed” with why; clicking it opens the app on the workflow’s canvas showing the failed run. With Settings › Appearance › Workflow tasks Off, nothing.
  • Past runs (the clock button). Expected: the workflow’s runs, newest first, with state, duration, cost and how each was started; picking one shows its receipt.
  • Select nothing on the canvas › Schedule › Add a schedule: every minute, input test. Expected: the schedule shows with its next run and a switch; within a minute the run starts by itself (status bar), its receipt says by a worker; Studio › Workers shows the worker <name>-schedule, and its view shows the run’s progress with Open on the canvas. Switch it off, change it, remove it.
  • Run a worker by hand (Run now), leave its view and come back while it runs. Expected: the view shows the run going (its events from the start) instead of only the worker.
  • Stop the service while a run goes, start it again, open the workflow’s Past runs. Expected: after about three minutes the run reads Stopped with “the service stopped while it ran”.
  • With no permission_mode in the settings, open a workspace. + in Studio › Workflows. Expected: the status bar’s mode is Auto; the new-workflow dialog offers Permission mode with Auto (the default) chosen, and the workflow made has it in its settings; the file has no mode: line.
  • Run a workflow with a save_file step (keys set: the reviewer is a model call). Expected: no approval card: the reviewer allows it (auto-allow in the audit log); the run panel’s summary ends with Auto.
  • Settings › Workspaces › the workspace › Behaviour › Permission mode: Ask. Restart the service, reopen the workspace, run the workflow again. Expected: the workspace is still Ask; the workflow’s settings say the workspace’s mode applies instead; the run asks for the save; its summary says Ask and its events “Asked for Auto; the workspace’s mode, Ask, applied”. A workflow set to Don’t ask refuses the save instead of asking.

53. Workspace health: Studio’s Overview (spec_health_044)

The facts cost nothing; a check makes one model call per item judged (cents for a small workspace). Use a test service with keys unset for the facts, and keys set only for the check.

  • Open Studio from the menu bar. Expected: it opens on Overview: the workspace’s ring (“Not judged yet”), a ring per kind with counts, the 30-day runs chart, eval coverage, Needs attention (an agent file with bad YAML shows as “doesn’t load”), and a card per kind. Opening a card shows its table; reopening Studio keeps it open.
  • With three workspaces open, open Studio, then click through every kind and back to Overview. Expected: every list loads at once; nothing waits (the Overview holds no connection open).
  • Re-check, then close Studio, and reopen it. Expected: the check went on: the bar shows where it is; when it ends, “Judged n items for $x” (or how many couldn’t be judged).
  • Re-check. Expected: a dialog says how many items and about what it costs; Judge shows “Judging 1 of n” with a bar and Stop; the kind rings and the workspace’s ring fill in as verdicts arrive; a snackbar says what it judged and cost. Each item in Studio’s lists shows its grade badge (a pulsing dot while it’s judged).
  • Re-check again without changing anything; then edit an agent and Re-check. Expected: “Every item is judged and unchanged”; then only that agent is judged, and its row shows a second point on its trend.
  • Open a kind’s card, click an item, then Improve on a fix. Expected: each rubric line’s score (pips) and reason, the summary, who judged and when; Improve closes Studio with the request in the composer.
  • The gear: schedule every 5 minutes, limit 0.0001. Expected: saved for the workspace; within a few minutes “The scheduled check didn’t run: judging n items would cost about $…, over the $0.00 limit”. With limit 0 it runs by itself. A new workspace that was never checked by hand is never checked on a schedule.

54. The composer: a workflow from a conversation (spec_composer_045)

Each turn is one small model call (well under a cent); the composer uses the suggestions model, else the auto reviewer’s.

  • Studio › Workflows › + › Compose with AI. Ask for “Every weekday, read qa/sales.csv, summarize the trend in two paragraphs and save it to reports/sales-summary.md”. Expected: “Thinking…”, then either a question with suggested answers as buttons, or straight away a draft: its name, its steps in order (read a file, summarize, save to a file) and no problems; the cost so far at the bottom.
  • Ask for something vague (“help with my reports”). Expected: a question, one at a time, with buttons; answering by a button or in your own words moves on; a draft comes by the fifth answer at the latest.
  • With a draft, Ask for a change (“also email it to me”). Expected: a new draft (email steps are coming soon, so the draft says what it can’t do, or shows it as a problem to fix by hand).
  • Open on the canvas. Expected: the canvas opens on a new, unsaved workflow with the draft’s name (a number added if it’s taken), its steps linked in order, and “Opened as a new workflow: review it, then save”; nothing is written until Save.
  • With the model unreachable (a wrong key), Start. Expected: the dialog shows the error; the status bar stays Connected (not “The Blitz service isn’t answering”).