The public-surface gate refuses any tracked file carrying a URL with a path outside .example, and21d7f5ashipped two: the python.org Windows downloads page and the Git for Windows download. Both are now named as bare domains for the tester to search for, which the scanner allows and which does not rot when a vendor moves a page. The gate found this and I did not, for a reason worth recording:21d7f5awas verified while the file was still UNTRACKED, and that scan reads `git ls-files`. The suite was genuinely green -- of a tree that did not yet contain the file. Verify after `git add`, not before, whenever a gate is scoped to tracked files. Also states the rule in the operator section, so the links are not restored as a courtesy later. Verify: .venv/bin/python -m pytest tests/ -> 223 passed, exit 0, with the file tracked. Scanner denominator 113 files / 609427 bytes, 0 findings; the byte count moved with the edit, so the file is inside the scan. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
519 lines
20 KiB
Markdown
519 lines
20 KiB
Markdown
# Windows smoke test
|
||
|
||
A checklist for a Windows tester. Follow it from top to bottom, in order, and
|
||
write down what you actually saw at each step. It assumes nothing: not that you
|
||
know a terminal, not that you use git, not that you know what an MCP server is.
|
||
|
||
Nothing here submits an application, sends an email, or types anything into a
|
||
job site. The whole plugin is read-only by design.
|
||
|
||
This is **not** a usability test. It is a platform measurement: seven questions
|
||
about whether this software can run on Windows at all. A "no" is a result, not
|
||
a failure, and it is worth more than a polite "worked fine". The most valuable
|
||
thing you can do is copy the exact text you saw instead of summarising it.
|
||
|
||
Expect it to take about 30 minutes.
|
||
|
||
---
|
||
|
||
## Before you send this out — operator only
|
||
|
||
The tester can skip this section and start at **Step 0**.
|
||
|
||
**BLOCKER F1 — do not send this checklist yet.** Measured 2026-09-06 on the
|
||
development Mac, against a tree assembled from `package_plugin.py`'s own
|
||
include list:
|
||
|
||
```
|
||
ERROR: /private/tmp/jobbsok-installed.EUmgm9/pyproject.toml not found.
|
||
Cannot resolve '--group' option.
|
||
bootstrap: interpreter /usr/local/bin/python3
|
||
bootstrap: environment /tmp/jobbsok-plugindata.lCctG2/venv [plugin-data ...]
|
||
exit=1
|
||
```
|
||
|
||
`scripts/bootstrap.py` installs its dependencies with
|
||
`pip install --group <repo root>/pyproject.toml:dev`, and `pyproject.toml` is
|
||
not in `package_plugin.py`'s `INCLUDE` tuple — so it is not in the archive. In
|
||
an installed copy the bootstrap therefore creates an empty virtualenv and then
|
||
fails, the ingestion guard is never installed, and the O4 gate refuses to
|
||
serve:
|
||
|
||
```
|
||
jobbsok-tools: <...>/venv/bin/python cannot import llm_ingestion_guard.
|
||
jobbsok-tools: run 'python scripts/bootstrap.py' against the installed plugin;
|
||
the guard is installed with it.
|
||
jobbsok-tools: refusing to start rather than serving tools that write without
|
||
the ingestion guard. Every skill still works by manual paste.
|
||
```
|
||
|
||
The refusal names the one command that cannot succeed in an installed copy.
|
||
This is platform-independent — it would fail identically on the Mac — so
|
||
sending the checklist now would buy a guaranteed "no" on **measurement 2**, the
|
||
one measurement this whole test exists for, for a reason that has nothing to do
|
||
with Windows. Two testers' time is the scarce resource; a spoiled critical
|
||
measurement has to be bought back with a second round.
|
||
|
||
Fixing it is a one-line change to `scripts/bootstrap.py`'s neighbourhood
|
||
(`INCLUDE`), which was outside the fence of the order that produced this file.
|
||
Steps 10 and 11 below are already written for the fixed build and name the
|
||
pre-fix error by its exact text, so the tester stops rather than reports a
|
||
misleading result. **Send this out only after that fix is in the build the
|
||
tester receives.**
|
||
|
||
Two more things to settle before sending:
|
||
|
||
* **Language.** This file is English, following `CLAUDE.md`'s "docs are
|
||
English" and matching `README.md`, which the tester reads anyway. The plugin
|
||
answers in Norwegian, so the chat phrases in Steps 12–14 are Norwegian
|
||
verbatim with a gloss. If the testers would be better served in Norwegian,
|
||
this is cheap to flip — but flip the whole file, not half of it.
|
||
* **What you send.** The `.plugin` file, its SHA-256, and the build stamp. The
|
||
tester compares all three; Steps 7 and 9 depend on you providing them.
|
||
|
||
**Why Steps 1 and 3 name download sites without linking them.**
|
||
`tests/test_public_surface.py` refuses any tracked file carrying a URL with a
|
||
path outside `.example`, and this remote is public. A bare domain in prose is
|
||
not a finding; `https://<site>/<path>` is. So the two sites are named for the
|
||
tester to search for. Do not "helpfully" restore the links — the gate will fail
|
||
the commit, and it is right to.
|
||
|
||
---
|
||
|
||
## What you need before you start
|
||
|
||
* A Windows PC — this test is only meaningful on Windows.
|
||
* The Claude desktop app, installed and signed in.
|
||
* The file `jobbsok.plugin`, sent to you, plus two short strings that came with
|
||
it: a **SHA-256 checksum** and a **build stamp** (seven characters, like
|
||
`c7db9f1`). You will compare against both.
|
||
* About 30 minutes.
|
||
|
||
Everything you type goes in **PowerShell**. To open it: press the Windows key,
|
||
type `PowerShell`, press Enter. A blue window opens. Type a command, press
|
||
Enter, read what comes back.
|
||
|
||
Copy each command exactly, including quotes and backslashes.
|
||
|
||
---
|
||
|
||
## How to report a problem
|
||
|
||
At any step where what you saw does not match "Expected", **stop and report**.
|
||
Do not work around it, and do not try the next step to see if it fixes itself.
|
||
|
||
A usable report has four parts. "It didn't work" is not one of them.
|
||
|
||
1. **The step number**, e.g. "Step 9".
|
||
2. **The command you ran**, copied exactly.
|
||
3. **The entire message you got back** — all of it, copy-pasted, not retyped,
|
||
not shortened, not described. If it was a window rather than text, a
|
||
screenshot.
|
||
4. **Your Windows version**, the line you wrote down in Step 0.
|
||
|
||
To copy text out of PowerShell: select it with the mouse, press Ctrl+C.
|
||
|
||
---
|
||
|
||
## Step 0 — Write down which Windows this is
|
||
|
||
```powershell
|
||
[System.Environment]::OSVersion.VersionString
|
||
```
|
||
|
||
**Expected:** one line, something like `Microsoft Windows NT 10.0.26100.0`.
|
||
|
||
Write it down. Every report you send needs it — the same command can behave
|
||
differently on two Windows builds, and without the version a report cannot be
|
||
reproduced.
|
||
|
||
---
|
||
|
||
## Step 1 — Is Python installed, and is it new enough?
|
||
|
||
```powershell
|
||
python --version
|
||
```
|
||
|
||
**Expected:** `Python 3.10.0` or any higher number, e.g. `Python 3.13.2`.
|
||
|
||
Other outcomes, and what to do:
|
||
|
||
* **`Python was not found; run without arguments to install from the Microsoft
|
||
Store...`** or the Microsoft Store opens — Python is not installed; what
|
||
answered was Windows' built-in shortcut. Install Python from the official
|
||
site — search for `python.org`, open its **Downloads** section and take the
|
||
Windows installer — and **tick "Add python.exe to PATH"** in the installer.
|
||
Then close PowerShell, open a **new** PowerShell window, and run the command
|
||
again.
|
||
* **`Python 3.9.x` or lower** — stop and report. The tool server refuses to run
|
||
below 3.10 on purpose, so this is a real answer, not a mistake you made.
|
||
* **`python : The term 'python' is not recognized...`** — stop and report,
|
||
including the whole message.
|
||
|
||
---
|
||
|
||
## Step 2 — Which Python file actually answered
|
||
|
||
```powershell
|
||
python -c "import sys; print(sys.executable)"
|
||
```
|
||
|
||
**Expected:** an absolute path ending in `python.exe`, for example
|
||
`C:\Users\<you>\AppData\Local\Programs\Python\Python313\python.exe`.
|
||
|
||
Write the whole path down. If it contains `WindowsApps`, note that too: it
|
||
means Python came from the Microsoft Store, and the `py` command will not
|
||
exist on your machine. Both are fine for this test; we just need to know which
|
||
one you have.
|
||
|
||
---
|
||
|
||
## Step 3 — Is git installed?
|
||
|
||
```powershell
|
||
git --version
|
||
```
|
||
|
||
**Expected:** a line like `git version 2.51.0.windows.1`.
|
||
|
||
If you get `git : The term 'git' is not recognized...`, install Git for
|
||
Windows from the official site — search for `git-scm.com` and take the Windows
|
||
download. The default answers in the installer are fine. Then open a **new**
|
||
PowerShell window and run the command again.
|
||
|
||
Git is needed for one thing only: the next step fetches a security component
|
||
from a code host.
|
||
|
||
---
|
||
|
||
## Step 4 — Can this machine reach the security component?
|
||
|
||
```powershell
|
||
git ls-remote https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git v1.3.0
|
||
```
|
||
|
||
**Expected:** exactly one line, a long hex string then a tab then the tag name:
|
||
|
||
```
|
||
748b8f9d846f17e9ab47551b803e62e74af5fb14 refs/tags/v1.3.0
|
||
```
|
||
|
||
The hash must be exactly that one — it identifies the pinned version.
|
||
|
||
If instead you get a timeout, a certificate error, a proxy error, or
|
||
`fatal: unable to access ...`, **stop and report the whole message**. That host
|
||
is public and needs no account, so a failure here is usually a corporate
|
||
firewall or proxy — and it is worth knowing, because the plugin refuses to run
|
||
its tool server without this component.
|
||
|
||
---
|
||
|
||
## Step 5 — Tell the plugin what Python is called here
|
||
|
||
On Windows, Python is normally called `python`. On Mac and Linux it is called
|
||
`python3`. The plugin has no way to spell it differently per platform, so it
|
||
reads one setting:
|
||
|
||
```powershell
|
||
setx JOBBSOK_LAUNCH_PYTHON python
|
||
```
|
||
|
||
**Expected:** `SUCCESS: Specified value was saved.`
|
||
|
||
This is permanent and only needs doing once. It takes effect in **new** windows
|
||
and **newly started** programs — a program that is already running does not see
|
||
it. That is why Step 8 restarts the app.
|
||
|
||
---
|
||
|
||
## Step 6 — Choose where your job-search folder lives
|
||
|
||
The plugin never guesses a location. Name one:
|
||
|
||
```powershell
|
||
setx JOBBSOK_WORKSPACE "$env:USERPROFILE\jobbsok-workspace"
|
||
```
|
||
|
||
**Expected:** `SUCCESS: Specified value was saved.`
|
||
|
||
Then create the folder itself:
|
||
|
||
```powershell
|
||
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\jobbsok-workspace"
|
||
```
|
||
|
||
**Expected:** a short table ending with a line naming `jobbsok-workspace`.
|
||
|
||
Now open a **new** PowerShell window (the old one cannot see the setting) and
|
||
check it:
|
||
|
||
```powershell
|
||
$env:JOBBSOK_WORKSPACE
|
||
```
|
||
|
||
**Expected:** a real path, e.g. `C:\Users\anna\jobbsok-workspace`.
|
||
|
||
**If it prints the literal text `%USERPROFILE%\jobbsok-workspace`**, the
|
||
setting was stored unexpanded. Re-run the `setx` line above exactly as written
|
||
— with `$env:USERPROFILE`, not `%USERPROFILE%` — in PowerShell, then check
|
||
again in another new window.
|
||
|
||
Write the path down. You will type it into the chat later.
|
||
|
||
---
|
||
|
||
## Step 7 — Install the plugin ▸ *measurement 1*
|
||
|
||
First, confirm you are testing the file that was actually sent to you:
|
||
|
||
```powershell
|
||
Get-FileHash "$env:USERPROFILE\Downloads\jobbsok.plugin" -Algorithm SHA256
|
||
```
|
||
|
||
(If you saved the file somewhere other than Downloads, change that path.)
|
||
|
||
**Expected:** a `Hash` value identical to the SHA-256 you were given. Compare
|
||
the whole string. If it differs, stop and report — the file changed in transit
|
||
and nothing measured after this would be about the right build.
|
||
|
||
Then, in the Claude desktop app: **Customize → Plugins**, choose the option to
|
||
install from a file, and pick `jobbsok.plugin`.
|
||
|
||
**Expected:** `jobbsok` appears in the plugin list, version `0.1.0`.
|
||
|
||
**If there is no way to install a plugin from a file on Windows** — no
|
||
Customize menu, no Plugins page, no upload option — stop, take a screenshot of
|
||
what you do see, and report. That is this measurement answering "no", and it is
|
||
a fact about the platform, not a mistake by you.
|
||
|
||
---
|
||
|
||
## Step 8 — Restart the app completely
|
||
|
||
Quit the Claude app fully — not just closing the window. Then start it again.
|
||
|
||
Programs read settings like the ones from Steps 5 and 6 when they start, so an
|
||
app that was already running has not seen them. Skipping this restart is the
|
||
most common way to get a false "no" in the next step.
|
||
|
||
---
|
||
|
||
## Step 9 — Does the connector appear? ▸ *measurement 2 — the critical one*
|
||
|
||
The plugin ships a small helper program that runs on your PC and that Claude
|
||
talks to. In the app it shows up as a **connector**, not as a skill.
|
||
|
||
In the app: **Customize → Plugins → jobbsok**, and look at its connectors.
|
||
|
||
**Expected:** `jobbsok-tools` is listed.
|
||
|
||
Write down **exactly one** of these three, word for word:
|
||
|
||
* `listed` — it appears.
|
||
* `not listed` — it does not appear at all.
|
||
* `listed but silent` — it appears, but Step 10 gets no answer from it.
|
||
|
||
**Do not try to fix a "no" here, and do not skip ahead.** This is the one
|
||
question the whole test exists to answer, and a "no" is a finding that changes
|
||
how the rest of this software gets built. Report it to the operator with your
|
||
Step 0 Windows version and a screenshot of the connectors page. It is not a bug
|
||
to be worked around in the moment.
|
||
|
||
Then, whichever you saw, continue — the remaining steps still measure things.
|
||
|
||
---
|
||
|
||
## Step 10 — Ask the plugin about itself ▸ *measurements 3, 4 and 5*
|
||
|
||
In the Claude chat, type this, replacing the path with the one from Step 6:
|
||
|
||
> Bruk verktøyet `selvsjekk` fra jobbsok-tools med workspace
|
||
> `C:\Users\<you>\jobbsok-workspace`
|
||
|
||
(*"Use the `selvsjekk` tool from jobbsok-tools with workspace ..."* — the
|
||
plugin answers in Norwegian.)
|
||
|
||
**Expected:** a block of JSON with these fields. Copy the whole thing into your
|
||
report; the notes below say what each one tells us.
|
||
|
||
| Field | Expected | What it tells us |
|
||
|---|---|---|
|
||
| `server` | `jobbsok-tools` | the right program answered |
|
||
| `server_versjon` | `0.1.0` | |
|
||
| `python_executable` | a path ending in `python.exe` | **measurement 5**: which interpreter really served, and whether it is the one from Step 2 |
|
||
| `python_version` | `3.10.0` or higher | |
|
||
| `guard_versjon` | `1.3.0` | the security component is present. `null` here needs reporting — see below |
|
||
| `plugin_root` | an absolute Windows path with a drive letter and backslashes | **measurement 4**: what `${CLAUDE_PLUGIN_ROOT}` became on Windows. Nobody has ever seen this value; write it down **verbatim** |
|
||
| `build_stamp` | the seven-character stamp you were given | you are testing the build that was sent, not a cached older one |
|
||
| `workspace` | the path from Step 6 | |
|
||
|
||
Report immediately, without continuing, if:
|
||
|
||
* **`plugin_root` prints the literal text `${CLAUDE_PLUGIN_ROOT}`** or is empty
|
||
— the variable did not resolve, which breaks every internal path in the
|
||
plugin, not just this one.
|
||
* **`build_stamp` differs from the stamp you were given** — the app served a
|
||
cached older build, so everything measured here is about some other version.
|
||
* **`guard_versjon` is `null`** — the server is running without the security
|
||
component. It is supposed to refuse to start at all in that state, so `null`
|
||
showing up here is itself the finding.
|
||
|
||
If nothing answers at all, or Claude says the tool is unavailable, go back to
|
||
Step 9 and write `listed but silent` (or keep `not listed`), then continue at
|
||
Step 11.
|
||
|
||
---
|
||
|
||
## Step 11 — Build the plugin's Python environment ▸ *measurement 6*
|
||
|
||
Find the installed copy on disk:
|
||
|
||
```powershell
|
||
Get-ChildItem -Path "$env:USERPROFILE\.claude\plugins" -Recurse -Filter "jobbsok_tools_launch.py" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName
|
||
```
|
||
|
||
**Expected:** one or more paths ending in `\jobbsok\scripts\jobbsok_tools_launch.py`.
|
||
|
||
If it prints nothing, report that — including the command — and skip to Step 12.
|
||
If Step 10 gave you a `plugin_root`, note whether it matches what you find here.
|
||
|
||
Go to the folder that contains `scripts` (the part of the path before
|
||
`\scripts\`), replacing the path below with your own:
|
||
|
||
```powershell
|
||
cd "C:\Users\<you>\.claude\plugins\<...>\jobbsok"
|
||
python scripts\bootstrap.py
|
||
```
|
||
|
||
This downloads a few components and takes a couple of minutes. Let it finish.
|
||
|
||
**Expected:** the last lines read approximately:
|
||
|
||
```
|
||
bootstrap: interpreter C:\...\python.exe
|
||
bootstrap: environment C:\...\.venv [repo root (development)]
|
||
bootstrap: sys.executable C:\...\.venv\Scripts\python.exe
|
||
bootstrap: python 3.13.2
|
||
bootstrap: guard 1.3.0
|
||
bootstrap: ok
|
||
```
|
||
|
||
Two notes so you do not report a non-problem:
|
||
|
||
* The `environment` line may say `[repo root (development)]` or
|
||
`[plugin-data (survives a plugin update)]`. **Either is fine here.** Which one
|
||
appears depends on a setting the app passes to its own programs and that your
|
||
PowerShell window does not have. Write down which one you saw.
|
||
* `bootstrap: guard 1.3.0` is the line that matters. It is the same version
|
||
Step 4 checked.
|
||
|
||
**Known failure — stop and report if you see it:**
|
||
|
||
```
|
||
ERROR: ...\pyproject.toml not found. Cannot resolve '--group' option.
|
||
```
|
||
|
||
That means you were sent a build made before a known fix. It is not your
|
||
machine and not your mistake. Report it and stop; the rest of the test needs a
|
||
newer build.
|
||
|
||
---
|
||
|
||
## Step 12 — Restart, and check the connector again
|
||
|
||
Quit the Claude app fully and start it again, then repeat **Step 9** and
|
||
**Step 10**.
|
||
|
||
**Expected:** `jobbsok-tools` is listed, and `guard_versjon` is now `1.3.0`.
|
||
|
||
Write down both answers again, side by side with the first pair. A connector
|
||
that was absent before Step 11 and present after it is a different — and much
|
||
better — result than one that was never there, and only the two readings
|
||
together can tell them apart.
|
||
|
||
---
|
||
|
||
## Step 13 — Create a case ▸ *first half of measurement 7*
|
||
|
||
In the chat, type (Norwegian, verbatim — this phrase is what starts the skill):
|
||
|
||
> Opprett en sak for arbeidsgiver Testbedriften AS, rolle Testrolle
|
||
|
||
(*"Create a case for employer Testbedriften AS, role Testrolle"*.)
|
||
|
||
**Expected:** Claude reports that a case was created and names a case id that
|
||
looks like `2026-09-testbedriften-as-testrolle`.
|
||
|
||
Check it landed on disk. In PowerShell:
|
||
|
||
```powershell
|
||
Get-ChildItem "$env:JOBBSOK_WORKSPACE\saker"
|
||
```
|
||
|
||
**Expected:** one folder, whose name is the case id Claude reported.
|
||
|
||
If nothing was created, report what Claude said, in full.
|
||
|
||
---
|
||
|
||
## Step 14 — Close everything, then ask again ▸ *second half of measurement 7*
|
||
|
||
1. Quit the Claude app completely.
|
||
2. Start it again and open a **new** chat — not the old one.
|
||
3. Type:
|
||
|
||
> Hva er status på saken?
|
||
|
||
(*"What is the status of the case?"*)
|
||
|
||
**Expected:** Claude names the same case id from Step 13, with status
|
||
`vurderer` and waiting party `meg`.
|
||
|
||
If the connector never appeared, you can still measure this half by hand.
|
||
Run, replacing the path:
|
||
|
||
```powershell
|
||
python scripts\sak_status.py --workspace "$env:USERPROFILE\jobbsok-workspace" --format tekst
|
||
```
|
||
|
||
from the plugin folder you found in Step 11.
|
||
|
||
**Expected:** a line naming the same case id and the status `vurderer`.
|
||
|
||
Say which of the two routes you used. They answer slightly different
|
||
questions — the chat route also proves the connector works, the command route
|
||
only proves the files survived.
|
||
|
||
---
|
||
|
||
## What each measurement means
|
||
|
||
| # | Question | Step | What a "no" would mean |
|
||
|---|---|---|---|
|
||
| 1 | Does the plugin install in Claude on Windows at all? | 7 | Nothing else is measurable; the delivery route itself has to change. |
|
||
| 2 | **Does `jobbsok-tools` appear as a connector?** | 9, 12 | **The critical one.** The design assumes Claude can run a helper program on your PC. If Windows cannot, every part of the plugin has to fall back to copy-and-paste, and the next four milestones get built differently. This is reported to the operator, never rounded off as "a bug". |
|
||
| 3 | Does `selvsjekk` answer, and what does it report about the interpreter, the build stamp and the security component? | 10 | A connector that is listed but mute is a different fault from one that is absent, and the two have different causes. |
|
||
| 4 | What does `${CLAUDE_PLUGIN_ROOT}` actually become on Windows? | 10 | It has never been observed on Windows. If it is not a real path with a drive letter and backslashes, every internal path in the plugin is affected. |
|
||
| 5 | Was `JOBBSOK_LAUNCH_PYTHON` needed, or did it start without? | 5, 10 | If it started without the setting, we need to know whether a real Python answered or the Microsoft Store shortcut did — Step 2's path is what tells them apart. |
|
||
| 6 | Does `bootstrap.py` build a working Python environment the plugin then finds? | 11, 12 | Without it there is no security component, and the tool server refuses to serve. This is also the step that has never run on Windows. |
|
||
| 7 | Does it remember a case between two sessions? | 13, 14 | If a case vanishes when the app restarts, the files were written somewhere temporary, and the whole case-folder model does not hold on this platform. |
|
||
|
||
---
|
||
|
||
## When you are done
|
||
|
||
Send back, in one message:
|
||
|
||
* Your Windows version from Step 0.
|
||
* The Python path from Step 2.
|
||
* Your Step 9 word (`listed` / `not listed` / `listed but silent`), both before
|
||
and after Step 11.
|
||
* The whole `selvsjekk` output from Step 10 (and from Step 12, if it changed).
|
||
* Which `environment` line Step 11 printed.
|
||
* Which route you used in Step 14, and whether the case was still there.
|
||
* Every "stop and report" you hit, with the four parts listed under
|
||
*How to report a problem*.
|
||
|
||
If everything matched, say so plainly and send the list anyway. A pass with the
|
||
actual values attached is evidence; a pass without them is a memory.
|