quickstarted 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quickstarted-0.2.0/.gitignore +14 -0
- quickstarted-0.2.0/CHANGELOG.md +72 -0
- quickstarted-0.2.0/CONTRIBUTING.md +56 -0
- quickstarted-0.2.0/DESIGN.md +106 -0
- quickstarted-0.2.0/LICENSE +21 -0
- quickstarted-0.2.0/PKG-INFO +256 -0
- quickstarted-0.2.0/README.md +211 -0
- quickstarted-0.2.0/RELEASING.md +67 -0
- quickstarted-0.2.0/SECURITY.md +48 -0
- quickstarted-0.2.0/docs/explanation/comparison.md +84 -0
- quickstarted-0.2.0/docs/explanation/proxy.md +76 -0
- quickstarted-0.2.0/docs/explanation/scoring.md +67 -0
- quickstarted-0.2.0/docs/getting-started/agent-mode.md +105 -0
- quickstarted-0.2.0/docs/getting-started/first-run.md +97 -0
- quickstarted-0.2.0/docs/getting-started/install.md +64 -0
- quickstarted-0.2.0/docs/guides/affordances.md +79 -0
- quickstarted-0.2.0/docs/guides/ci.md +125 -0
- quickstarted-0.2.0/docs/guides/cost.md +92 -0
- quickstarted-0.2.0/docs/guides/pass-rates.md +86 -0
- quickstarted-0.2.0/docs/guides/sandboxing.md +99 -0
- quickstarted-0.2.0/docs/guides/writing-journeys.md +132 -0
- quickstarted-0.2.0/docs/index.md +73 -0
- quickstarted-0.2.0/docs/reference/cli.md +102 -0
- quickstarted-0.2.0/docs/reference/environment.md +66 -0
- quickstarted-0.2.0/docs/reference/journey-schema.md +112 -0
- quickstarted-0.2.0/docs/reference/results.md +112 -0
- quickstarted-0.2.0/docs/reference/trace.md +86 -0
- quickstarted-0.2.0/docs_hooks.py +81 -0
- quickstarted-0.2.0/journeys/bottomtime-quickstart.yaml +22 -0
- quickstarted-0.2.0/journeys/duckdb-quickstart.yaml +45 -0
- quickstarted-0.2.0/journeys/fastapi-quickstart.yaml +52 -0
- quickstarted-0.2.0/journeys/httpx-quickstart.yaml +31 -0
- quickstarted-0.2.0/journeys/pnf-quickstart.yaml +26 -0
- quickstarted-0.2.0/journeys/polars-quickstart.yaml +40 -0
- quickstarted-0.2.0/journeys/quickstarted-quickstart.yaml +44 -0
- quickstarted-0.2.0/journeys/streamlit-quickstart.yaml +53 -0
- quickstarted-0.2.0/mkdocs.yml +82 -0
- quickstarted-0.2.0/pyproject.toml +121 -0
- quickstarted-0.2.0/src/quickstarted/__init__.py +24 -0
- quickstarted-0.2.0/src/quickstarted/_version.py +7 -0
- quickstarted-0.2.0/src/quickstarted/agents/__init__.py +4 -0
- quickstarted-0.2.0/src/quickstarted/agents/base.py +145 -0
- quickstarted-0.2.0/src/quickstarted/agents/claude.py +242 -0
- quickstarted-0.2.0/src/quickstarted/agents/gemini_agent.py +138 -0
- quickstarted-0.2.0/src/quickstarted/agents/openai_agent.py +167 -0
- quickstarted-0.2.0/src/quickstarted/agents/prompt.py +61 -0
- quickstarted-0.2.0/src/quickstarted/agents/registry.py +31 -0
- quickstarted-0.2.0/src/quickstarted/agents/replay.py +39 -0
- quickstarted-0.2.0/src/quickstarted/cli.py +250 -0
- quickstarted-0.2.0/src/quickstarted/docs.py +263 -0
- quickstarted-0.2.0/src/quickstarted/exec/__init__.py +89 -0
- quickstarted-0.2.0/src/quickstarted/exec/base.py +131 -0
- quickstarted-0.2.0/src/quickstarted/exec/docker.py +216 -0
- quickstarted-0.2.0/src/quickstarted/exec/local.py +16 -0
- quickstarted-0.2.0/src/quickstarted/exec/seatbelt.py +83 -0
- quickstarted-0.2.0/src/quickstarted/journey.py +182 -0
- quickstarted-0.2.0/src/quickstarted/net/__init__.py +5 -0
- quickstarted-0.2.0/src/quickstarted/net/proxy.py +274 -0
- quickstarted-0.2.0/src/quickstarted/net/proxy_main.py +53 -0
- quickstarted-0.2.0/src/quickstarted/pricing.py +93 -0
- quickstarted-0.2.0/src/quickstarted/report.py +206 -0
- quickstarted-0.2.0/src/quickstarted/results.py +166 -0
- quickstarted-0.2.0/src/quickstarted/run.py +240 -0
- quickstarted-0.2.0/src/quickstarted/sandbox.py +15 -0
- quickstarted-0.2.0/src/quickstarted/suite.py +203 -0
- quickstarted-0.2.0/src/quickstarted/trace.py +53 -0
- quickstarted-0.2.0/src/quickstarted/transport.py +88 -0
- quickstarted-0.2.0/tests/test_agents.py +133 -0
- quickstarted-0.2.0/tests/test_claude_credentials.py +95 -0
- quickstarted-0.2.0/tests/test_cli.py +85 -0
- quickstarted-0.2.0/tests/test_docker_backend.py +211 -0
- quickstarted-0.2.0/tests/test_docs.py +145 -0
- quickstarted-0.2.0/tests/test_exec.py +127 -0
- quickstarted-0.2.0/tests/test_journey.py +123 -0
- quickstarted-0.2.0/tests/test_proxy.py +117 -0
- quickstarted-0.2.0/tests/test_run.py +155 -0
- quickstarted-0.2.0/tests/test_sandbox.py +65 -0
- quickstarted-0.2.0/tests/test_suite.py +256 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. This project follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [0.2.0] - 2026-07-26
|
|
7
|
+
|
|
8
|
+
The v0.1 harness could be talked out of its own guarantees: the docs allowlist
|
|
9
|
+
lived in the `read_docs` tool, so any agent that ran `curl` read pages the
|
|
10
|
+
trace never saw, and commands ran unconfined on the host. This release makes
|
|
11
|
+
the boundary real and makes the output publishable.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Execution backends** (`quickstarted.exec`): `docker`, `seatbelt` (macOS), and
|
|
16
|
+
`local`. `auto` prefers an enforced backend. `quickstarted run` refuses the
|
|
17
|
+
unenforced one unless `--allow-unenforced` is given. Both enforced backends
|
|
18
|
+
are verified against live daemons, including tests asserting that a direct
|
|
19
|
+
connection bypassing the proxy fails.
|
|
20
|
+
- **Egress proxy** (`quickstarted.net`): all shell traffic leaves through a
|
|
21
|
+
harness-owned proxy. Documentation hosts are unreachable from the shell, so
|
|
22
|
+
the recorded set of pages read is complete; attempts to bypass are counted.
|
|
23
|
+
`network.allow` in a journey names hosts a shell may reach for installs.
|
|
24
|
+
- **Run classification**: `passed`, `docs_gap`, `budget_exhausted`,
|
|
25
|
+
`infra_error`, `harness_error`, `agent_refusal`. Only the first two are
|
|
26
|
+
evidence about documentation. The rest are excluded from pass rates, and
|
|
27
|
+
reported separately.
|
|
28
|
+
- **Pass rates**: `--repeat N` and `--workers N`. A single run is one sample,
|
|
29
|
+
and the rate over several is the number worth reading.
|
|
30
|
+
- **Affordance policy**: `--affordances all|none` withholds `llms.txt` and
|
|
31
|
+
`.md` variants so the same journey can be run both ways and the difference
|
|
32
|
+
measured. `--probe-affordances` records what exists. Never scored.
|
|
33
|
+
- **Docs cache and politeness**: `--cache-dir`, `--refresh` (flags content
|
|
34
|
+
that changed between runs), `--offline`, `--rate-limit`, `--ignore-robots`.
|
|
35
|
+
Truthful User-Agent, robots.txt honoured by default.
|
|
36
|
+
- **Machine-readable output**: versioned `results.json` (schema 1.0) and
|
|
37
|
+
`--junit` XML, where a docs gap is a failure and an infrastructure problem is
|
|
38
|
+
an error.
|
|
39
|
+
- **More agents**: `openai` and `gemini` adapters alongside `claude`, sharing
|
|
40
|
+
one prompt so cross-model numbers compare like with like. Neither assumes a
|
|
41
|
+
default model. Token counts are normalised across vendors: OpenAI and Google
|
|
42
|
+
report a prompt total that includes cached tokens while Anthropic excludes
|
|
43
|
+
them, so adapters subtract the overlap and the four counters never bill the
|
|
44
|
+
same token twice.
|
|
45
|
+
- **Retries** with exponential backoff on transient upstream faults. Every wait
|
|
46
|
+
lands in the trace instead of being absorbed silently by the SDK.
|
|
47
|
+
- **Token budgets** (`budgets.max_tokens`) and optional cost estimation from a
|
|
48
|
+
price book you supply (`--prices`). No prices ship with the tool.
|
|
49
|
+
- `quickstarted doctor`, reporting what this machine can enforce.
|
|
50
|
+
- Tests: proxy policy, Seatbelt confinement, classification, statistics,
|
|
51
|
+
caching, robots, and the affordance ablation.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- Cached prompt tokens are counted. Previously a run costing ~120k cache-read
|
|
56
|
+
tokens reported "22 in" and looked free.
|
|
57
|
+
- The Claude adapter defaults to `claude-opus-5` and records the exact model
|
|
58
|
+
the API served, since an alias can change under a benchmark.
|
|
59
|
+
- Agents are told what `setup` already did, so they stop rebuilding a
|
|
60
|
+
virtualenv that exists.
|
|
61
|
+
- Journeys no longer list package registries under `docs.allow`; `validate`
|
|
62
|
+
warns when they do, because the proxy will refuse `pip`.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- `Trace` is thread-safe; the proxy appends from its own threads.
|
|
67
|
+
|
|
68
|
+
## [0.1.0] - 2026-07-24
|
|
69
|
+
|
|
70
|
+
Initial release: journey spec, replay and Claude agent modes, sandboxed
|
|
71
|
+
execution, docs fetch with attribution, deterministic scoring, CLI and
|
|
72
|
+
GitHub Action.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
python3 -m venv .venv
|
|
7
|
+
.venv/bin/pip install -e ".[dev]"
|
|
8
|
+
.venv/bin/pytest
|
|
9
|
+
.venv/bin/ruff check .
|
|
10
|
+
.venv/bin/mypy
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Tests that touch the network are skipped unless `QUICKSTARTED_NETWORK_TESTS=1`
|
|
14
|
+
is set. No test needs an API key.
|
|
15
|
+
|
|
16
|
+
## Writing a journey
|
|
17
|
+
|
|
18
|
+
A good journey asserts an outcome the documentation actually promises, and
|
|
19
|
+
nothing else.
|
|
20
|
+
|
|
21
|
+
- **Assert the data.** Check that `out.csv` holds the right rows. Do not check
|
|
22
|
+
which function the agent called; any correct route should pass.
|
|
23
|
+
- **Do not assert incidental paths.** An early journey checked for a `.venv`
|
|
24
|
+
the docs never mention; an agent that made `venv/` "failed" for no reason.
|
|
25
|
+
If `setup` creates something, the agent is told, and the check may rely on
|
|
26
|
+
it.
|
|
27
|
+
- **Let the harness verify.** If the goal is a running server, the success
|
|
28
|
+
script starts it, asks it a question, and stops it. Do not trust the agent's
|
|
29
|
+
report, and do not ask the agent to leave something running.
|
|
30
|
+
- **Separate docs hosts from registries.** `docs.allow` hosts are readable only
|
|
31
|
+
through `read_docs`; a shell cannot reach them. Putting `pypi.org` there
|
|
32
|
+
stops `pip` working. `quickstarted validate` warns about this.
|
|
33
|
+
|
|
34
|
+
Run `quickstarted run <journey> --agent replay` first. If the documented
|
|
35
|
+
commands do not pass, the journey is not ready for agent mode.
|
|
36
|
+
|
|
37
|
+
## Adding an agent adapter
|
|
38
|
+
|
|
39
|
+
Implement `run(journey, toolbelt, deadline) -> AgentOutcome` and act only
|
|
40
|
+
through the toolbelt: never touch the filesystem or network directly, or the
|
|
41
|
+
run loses the attribution and enforcement the whole tool depends on.
|
|
42
|
+
|
|
43
|
+
Use `agents/prompt.py` unchanged. Adapters that phrase the task their own way
|
|
44
|
+
turn a cross-model comparison into a comparison of prompts.
|
|
45
|
+
|
|
46
|
+
Do not assume a default model for a new vendor. Model names change faster than
|
|
47
|
+
a pinned default survives, and a benchmark that silently picks one produces
|
|
48
|
+
numbers nobody can reproduce.
|
|
49
|
+
|
|
50
|
+
## Things that will get a change rejected
|
|
51
|
+
|
|
52
|
+
- An LLM anywhere in the scoring path.
|
|
53
|
+
- Scoring the presence of `llms.txt`, MCP, or any other affordance. Measure it
|
|
54
|
+
with an ablation instead.
|
|
55
|
+
- Counting infrastructure failures as documentation failures.
|
|
56
|
+
- Making the unenforced backend the silent default.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Design notes
|
|
2
|
+
|
|
3
|
+
## The one decision everything follows from
|
|
4
|
+
|
|
5
|
+
Deterministic assertions are the only pass/fail truth. The success script runs
|
|
6
|
+
after the agent stops, in the same workspace, and its exit code is the
|
|
7
|
+
verdict. LLM judges may eventually classify WHY a run failed; they will never
|
|
8
|
+
decide WHETHER it failed. This keeps the tool falsifiable and keeps vendors
|
|
9
|
+
(including Anthropic) out of the scoring path.
|
|
10
|
+
|
|
11
|
+
## Attribution has to be enforced, not requested
|
|
12
|
+
|
|
13
|
+
Agents act only through the Toolbelt (`bash` + `read_docs`), so every page read
|
|
14
|
+
is recorded and failures attribute to a page.
|
|
15
|
+
|
|
16
|
+
v0.1 stopped there, and that was not enough. The allowlist lived in the
|
|
17
|
+
`read_docs` tool while `bash` had unrestricted network access, so any agent
|
|
18
|
+
that ran `curl` read documentation the trace never saw. The guarantee held only
|
|
19
|
+
while the agent cooperated, which is not a guarantee.
|
|
20
|
+
|
|
21
|
+
Now all shell traffic leaves through a proxy the harness owns, and
|
|
22
|
+
documentation hosts are unreachable from the shell. An attempted `curl` to a
|
|
23
|
+
docs host is refused and counted. The set of pages in the report is the set of
|
|
24
|
+
pages the agent read.
|
|
25
|
+
|
|
26
|
+
This creates one deliberate asymmetry: `docs.allow` hosts are readable only via
|
|
27
|
+
`read_docs`, and `network.allow` hosts (package registries) are reachable only
|
|
28
|
+
from the shell. When a host is genuinely both, PyPI being the obvious case, the
|
|
29
|
+
journey says so explicitly and installs win. The resulting gap in attribution
|
|
30
|
+
is reported alongside the result.
|
|
31
|
+
|
|
32
|
+
## Backends, and saying which one ran
|
|
33
|
+
|
|
34
|
+
Three: `docker` (container on an internal network whose only route out is the
|
|
35
|
+
proxy sidecar), `seatbelt` (macOS, kernel-enforced, all egress denied except
|
|
36
|
+
the proxy port, home directory unreadable), and `local` (nothing enforced).
|
|
37
|
+
|
|
38
|
+
`local` is refused unless explicitly allowed, every result records which
|
|
39
|
+
backend ran and whether it was enforced, and `quickstarted doctor` says what a
|
|
40
|
+
machine can do before anyone trusts its numbers. An unenforced result still
|
|
41
|
+
tells you something. It just supports a weaker claim, and the claim should
|
|
42
|
+
travel with the number.
|
|
43
|
+
|
|
44
|
+
## A run is a sample; a rate is a measurement
|
|
45
|
+
|
|
46
|
+
The same docs and model can pass and then fail. `--repeat` produces a pass
|
|
47
|
+
rate, and every run is classified. Only `passed` and `docs_gap` are statements
|
|
48
|
+
about documentation. Rate limits, exhausted budgets, refusals, and our own bugs
|
|
49
|
+
are excluded from both the numerator and the denominator and reported
|
|
50
|
+
separately.
|
|
51
|
+
|
|
52
|
+
A sweep of fifty projects will hit 429s and flaky networks. Reporting those as
|
|
53
|
+
failed quickstarts would be a lie that is very easy to tell, and it is the
|
|
54
|
+
difference between a benchmark and a leaderboard-shaped rumour.
|
|
55
|
+
|
|
56
|
+
## Affordances are measured, never scored
|
|
57
|
+
|
|
58
|
+
Whether a project ships `llms.txt` is a checklist item anyone can `curl`, and
|
|
59
|
+
scoring it would be the proxy metric this tool exists to replace. So presence
|
|
60
|
+
is recorded as context, with sizes, and the affordance can be *withheld*:
|
|
61
|
+
`--affordances none` blocks `llms.txt` and `.md` variants while keeping the
|
|
62
|
+
prompt byte-identical. Run both conditions and the difference in pass rate is a
|
|
63
|
+
measurement of the affordance itself.
|
|
64
|
+
|
|
65
|
+
That question is currently argued without data. This is the only shape of tool
|
|
66
|
+
that can answer it, because answering it requires running the task.
|
|
67
|
+
|
|
68
|
+
## Replay mode is the floor, not a feature
|
|
69
|
+
|
|
70
|
+
Replay (documented commands verbatim, no LLM) is free, deterministic and
|
|
71
|
+
CI-friendly, and it defines a floor: if replay fails, agent mode is noise. It
|
|
72
|
+
is a weaker `runShell` than a docs-testing framework like Doc Detective
|
|
73
|
+
already offers. Sell it as a precondition, and let agent mode carry the
|
|
74
|
+
product.
|
|
75
|
+
|
|
76
|
+
## Trace as the product surface
|
|
77
|
+
|
|
78
|
+
Everything interesting is an event in the JSONL trace: setup, tool calls, docs
|
|
79
|
+
fetches with content hashes, egress decisions, agent turns with token usage,
|
|
80
|
+
retries, the success check. `results.json` (schema 1.0) is the stable contract
|
|
81
|
+
over those traces. The hosted product is, to a first approximation, storage
|
|
82
|
+
plus diffing plus alerting across models and time.
|
|
83
|
+
|
|
84
|
+
## Cost is reported in tokens
|
|
85
|
+
|
|
86
|
+
Cached prompt tokens are excluded from `input_tokens` by the API, so summing
|
|
87
|
+
that field alone made a run costing 120k cache-read tokens look free. All four
|
|
88
|
+
counters are tracked. Dollars appear only when the operator supplies a price
|
|
89
|
+
book: vendor rates change, and a stale table baked into a benchmarking tool
|
|
90
|
+
would quietly misreport what a sweep costs.
|
|
91
|
+
|
|
92
|
+
## Known gaps
|
|
93
|
+
|
|
94
|
+
- Both enforced backends are verified end to end against live daemons. In the
|
|
95
|
+
container the isolation is stronger than Seatbelt's: a direct dial to an IP
|
|
96
|
+
address fails with `Network is unreachable`. The sandbox network has no route
|
|
97
|
+
out at all, so the isolation does not depend on name resolution.
|
|
98
|
+
- HTML-to-text is a crude tag stripper, so markdown-native docs read better.
|
|
99
|
+
That is itself a finding worth publishing.
|
|
100
|
+
- Concurrency is per-attempt threads, not a scheduler; hosts are rate-limited
|
|
101
|
+
individually but there is no global budget governor.
|
|
102
|
+
- No pip/npm cache; every run cold-installs.
|
|
103
|
+
- The Gemini adapter is written against the same Toolbelt contract but has not
|
|
104
|
+
been run live; Claude and OpenAI have.
|
|
105
|
+
- Nothing distinguishes "the docs are wrong" from "the product is broken". Both
|
|
106
|
+
surface as `docs_gap` and need a human.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Snehan Kekre
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quickstarted
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Test whether an AI agent can actually complete your quickstart by following your docs
|
|
5
|
+
Project-URL: Homepage, https://github.com/snehankekre/quickstarted
|
|
6
|
+
Project-URL: Issues, https://github.com/snehankekre/quickstarted/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/snehankekre/quickstarted/blob/main/CHANGELOG.md
|
|
8
|
+
Author-email: snehan <snehan.minerva@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent-experience,agents,ci,devrel,docs,documentation,testing
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Provides-Extra: all-agents
|
|
25
|
+
Requires-Dist: anthropic>=0.40; extra == 'all-agents'
|
|
26
|
+
Requires-Dist: google-genai>=1.0; extra == 'all-agents'
|
|
27
|
+
Requires-Dist: openai>=1.40; extra == 'all-agents'
|
|
28
|
+
Provides-Extra: claude
|
|
29
|
+
Requires-Dist: anthropic>=0.40; extra == 'claude'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: anthropic>=0.40; extra == 'dev'
|
|
32
|
+
Requires-Dist: google-genai>=1.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
34
|
+
Requires-Dist: openai>=1.40; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
38
|
+
Provides-Extra: docs
|
|
39
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
40
|
+
Provides-Extra: gemini
|
|
41
|
+
Requires-Dist: google-genai>=1.0; extra == 'gemini'
|
|
42
|
+
Provides-Extra: openai
|
|
43
|
+
Requires-Dist: openai>=1.40; extra == 'openai'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# quickstarted
|
|
47
|
+
|
|
48
|
+
Test whether an AI agent can actually complete your quickstart by following
|
|
49
|
+
your docs.
|
|
50
|
+
|
|
51
|
+
[](https://github.com/snehankekre/quickstarted/actions/workflows/ci.yml)
|
|
52
|
+
[](https://pypi.org/project/quickstarted/)
|
|
53
|
+
[](https://pypi.org/project/quickstarted/)
|
|
54
|
+
[](LICENSE)
|
|
55
|
+
|
|
56
|
+
Roughly half the traffic to documentation sites is now AI agents and
|
|
57
|
+
AI-assisted workflows ([Mintlify's measurement](https://www.mintlify.com/blog/ai-traffic)).
|
|
58
|
+
When an agent reads your quickstart and fails, you lose the developer behind
|
|
59
|
+
it and you never find out. quickstarted turns that failure into a CI signal: a
|
|
60
|
+
sandboxed agent gets your goal, your docs, and nothing else, and a script you
|
|
61
|
+
wrote decides whether it succeeded.
|
|
62
|
+
|
|
63
|
+
Full documentation: **https://snehankekre.com/quickstarted/**
|
|
64
|
+
|
|
65
|
+
**Status: early. The interface will change.**
|
|
66
|
+
|
|
67
|
+
## How it works
|
|
68
|
+
|
|
69
|
+
You write a journey, in YAML:
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
name: pnf-quickstart
|
|
73
|
+
goal: >
|
|
74
|
+
Install the pnf package from PyPI into a virtualenv and write a runnable
|
|
75
|
+
snippet that imports pnf and identifies the decode function.
|
|
76
|
+
docs:
|
|
77
|
+
entrypoint: https://raw.githubusercontent.com/snehankekre/pnf/main/README.md
|
|
78
|
+
allow:
|
|
79
|
+
- raw.githubusercontent.com
|
|
80
|
+
setup:
|
|
81
|
+
- python3 -m venv .venv
|
|
82
|
+
success:
|
|
83
|
+
script: |
|
|
84
|
+
set -e
|
|
85
|
+
.venv/bin/python -c "import pnf; assert callable(pnf.decode)"
|
|
86
|
+
.venv/bin/python demo.py
|
|
87
|
+
replay:
|
|
88
|
+
- .venv/bin/pip install pnf
|
|
89
|
+
- printf 'import pnf\nprint(pnf.decode.__name__)\n' > demo.py
|
|
90
|
+
- .venv/bin/python demo.py
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The harness runs an agent in a throwaway workspace with two tools: a sandboxed
|
|
94
|
+
`bash`, and `read_docs`, which is the only way it can read documentation.
|
|
95
|
+
|
|
96
|
+
The harness enforces that. All shell traffic leaves through a proxy it owns,
|
|
97
|
+
and documentation hosts are unreachable from the shell, so an agent that tries
|
|
98
|
+
`curl https://docs.example.com/...` is refused and the attempt is recorded.
|
|
99
|
+
The pages listed in a report are therefore the pages the agent really read.
|
|
100
|
+
When a run fails, the last page it read is a fact you can act on.
|
|
101
|
+
|
|
102
|
+
When the agent stops, the harness runs your `success.script` in the workspace.
|
|
103
|
+
Exit code 0 is a pass. Nothing else is. The agent's own claim of success is
|
|
104
|
+
recorded and never trusted; there is no LLM judge anywhere in scoring.
|
|
105
|
+
|
|
106
|
+
## Two modes
|
|
107
|
+
|
|
108
|
+
**Replay mode** (free, no LLM, no API key) runs your `replay` commands, the
|
|
109
|
+
literal commands your docs tell users to type, and stops at the first failure.
|
|
110
|
+
Treat it as a precondition. If the documented commands break, no reader stands
|
|
111
|
+
a chance, and agent mode only adds noise.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
quickstarted run journeys/pnf-quickstart.yaml --agent replay
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Agent mode** has an LLM follow your docs to the goal. This catches what
|
|
118
|
+
replay cannot: ambiguity, missing steps, wrong ordering, docs that assume
|
|
119
|
+
context a newcomer does not have.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
pip install "quickstarted[claude]"
|
|
123
|
+
export QUICKSTARTED_ANTHROPIC_API_KEY=...
|
|
124
|
+
quickstarted run journeys/pnf-quickstart.yaml --agent claude
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Keys are read from `QUICKSTARTED_*` names first so they can sit in your shell
|
|
128
|
+
without other tooling on the same machine picking them up and spending them.
|
|
129
|
+
The vendor-standard names still work as a fallback, which is what CI sets.
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
[PASS] pnf-quickstart (claude:claude-opus-5)
|
|
133
|
+
classification: passed
|
|
134
|
+
turns: 12, duration: 70.7s
|
|
135
|
+
backend: seatbelt
|
|
136
|
+
tokens: 24 in / 4616 out, cache 16812 written / 104148 read
|
|
137
|
+
docs pages read: 2
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Pass rates
|
|
141
|
+
|
|
142
|
+
The same docs and the same model can pass at 10:00 and fail at 10:05. One run
|
|
143
|
+
is a sample. Use `--repeat` and read the rate:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
quickstarted run journeys/*.yaml --agent claude --repeat 5 --workers 3
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Every run is classified, and only two classifications say anything about your
|
|
150
|
+
documentation:
|
|
151
|
+
|
|
152
|
+
| Classification | Meaning | Counts toward pass rate |
|
|
153
|
+
| --- | --- | --- |
|
|
154
|
+
| `passed` | success script exited 0 | yes |
|
|
155
|
+
| `docs_gap` | the agent finished, the check failed | yes |
|
|
156
|
+
| `budget_exhausted` | out of turns, time, or tokens | no |
|
|
157
|
+
| `infra_error` | rate limit, upstream 5xx, network failure | no |
|
|
158
|
+
| `harness_error` | misconfigured journey, our bug | no |
|
|
159
|
+
| `agent_refusal` | model declined | no |
|
|
160
|
+
|
|
161
|
+
Runs that produced no evidence are excluded from the numerator *and* the
|
|
162
|
+
denominator, and reported separately. When nothing produced evidence the suite
|
|
163
|
+
says so. Reporting 0% there would be a different claim, and a false one.
|
|
164
|
+
|
|
165
|
+
## Does llms.txt actually help?
|
|
166
|
+
|
|
167
|
+
Whether a project ships `llms.txt` is a checklist item anyone can `curl`, and
|
|
168
|
+
scoring it would be the proxy metric this tool exists to replace. So
|
|
169
|
+
quickstarted never scores affordances. It measures them:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
quickstarted run journeys/streamlit-quickstart.yaml --agent claude --repeat 10
|
|
173
|
+
quickstarted run journeys/streamlit-quickstart.yaml --agent claude --repeat 10 --affordances none
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Same prompt, same journey, same model. The only difference is whether the
|
|
177
|
+
agent could read `llms.txt` and `.md` variants. The difference in pass rate is
|
|
178
|
+
a measurement of the affordance itself.
|
|
179
|
+
|
|
180
|
+
`--probe-affordances` records what exists without changing anything, including
|
|
181
|
+
the size. Presence and usefulness come apart quickly: Streamlit's `llms.txt` is
|
|
182
|
+
67 KB, while its `llms-full.txt` is 1.8 MB, which is more than most agents can
|
|
183
|
+
read in one go.
|
|
184
|
+
|
|
185
|
+
## Sandboxing
|
|
186
|
+
|
|
187
|
+
Agent-authored commands from somebody else's quickstart are untrusted code.
|
|
188
|
+
|
|
189
|
+
| Backend | Filesystem | Network |
|
|
190
|
+
| --- | --- | --- |
|
|
191
|
+
| `docker` | container | internal network; the proxy sidecar is the only route out |
|
|
192
|
+
| `seatbelt` (macOS) | home directory unreadable, writes confined to the workspace | all egress denied except the harness proxy |
|
|
193
|
+
| `local` | none | none; proxy variables are advisory |
|
|
194
|
+
|
|
195
|
+
`auto` picks the best available. `quickstarted run` refuses `local` unless you
|
|
196
|
+
pass `--allow-unenforced`. Run `quickstarted doctor` to see what your machine
|
|
197
|
+
can enforce. Details in [SECURITY.md](SECURITY.md).
|
|
198
|
+
|
|
199
|
+
## Install
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
pip install quickstarted # replay mode only
|
|
203
|
+
pip install "quickstarted[claude]" # + Claude
|
|
204
|
+
pip install "quickstarted[all-agents]" # + OpenAI and Gemini
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Python 3.9+. One runtime dependency: PyYAML. Every adapter is a plain tool-use
|
|
208
|
+
loop against the same two harness-owned tools and the same shared prompt, so
|
|
209
|
+
cross-model numbers compare like with like.
|
|
210
|
+
|
|
211
|
+
## CI
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
- uses: actions/checkout@v4
|
|
215
|
+
- uses: actions/setup-python@v5
|
|
216
|
+
with:
|
|
217
|
+
python-version: "3.12"
|
|
218
|
+
- run: pip install quickstarted
|
|
219
|
+
- run: quickstarted run journeys/*.yaml --agent replay --out results --junit junit.xml
|
|
220
|
+
- uses: actions/upload-artifact@v4
|
|
221
|
+
if: always()
|
|
222
|
+
with:
|
|
223
|
+
name: quickstarted-results
|
|
224
|
+
path: results/
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`quickstarted run` exits 1 when any journey fails, so the job gates merges.
|
|
228
|
+
`results.json` is versioned (schema 1.0). JUnit XML reports a docs gap as a
|
|
229
|
+
failure and infrastructure trouble as an error, so a rate limit does not read
|
|
230
|
+
as a broken quickstart. Run agent mode on a schedule, or when a model ships.
|
|
231
|
+
Every push is too often, because agent runs cost real tokens.
|
|
232
|
+
|
|
233
|
+
## Fetching other people's docs
|
|
234
|
+
|
|
235
|
+
Benchmarking means requesting pages from organisations who did not ask to be
|
|
236
|
+
measured. quickstarted sends a truthful User-Agent, honours `robots.txt`, and
|
|
237
|
+
waits a second between requests to the same host. `--cache-dir` makes reruns
|
|
238
|
+
read the same bytes. `--refresh` flags pages whose content changed since the
|
|
239
|
+
last run, which is a finding in its own right.
|
|
240
|
+
|
|
241
|
+
## What this does and does not tell you
|
|
242
|
+
|
|
243
|
+
It tells you whether a given model, on a given day, could get from your docs to
|
|
244
|
+
a working result, and where it was reading when it could not. That is a lower
|
|
245
|
+
bound on documentation quality. Nothing here measures whether your docs are
|
|
246
|
+
pleasant, complete, or accurate beyond the journey you wrote, and there is no
|
|
247
|
+
UI testing at all: if your product's button moved, this will not notice.
|
|
248
|
+
[Doc Detective](https://github.com/doc-detective/doc-detective) is the tool for
|
|
249
|
+
that, and the two answer different questions.
|
|
250
|
+
|
|
251
|
+
Write success scripts that assert local, deterministic facts. A pass means the
|
|
252
|
+
floor holds, and says nothing about the ceiling.
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
MIT.
|