Skip to main content

Building libraries and tools with an agent

The agent that writes your tests can also write a Robot Framework library or tool: keywords for your own system, a listener, an integration with a test-management service. It does that well when it starts from the right context, and badly when it starts from what it remembers. This page is the method of the two bonus labs, Bonus 1 and Bonus 2. The references are for Robot Framework 7.5, the version this workshop pins.

1. Start outside every other project​

Agents collect instructions from more than the folder they start in:

  • Claude Code reads CLAUDE.md from the working folder and every folder above it.
  • Codex reads AGENTS.md from the git repository's root down to the working folder.
  • OpenSpec uses the nearest openspec/ folder.

A library created inside the workshop clone would get the clone's rules: the demo shop, the test conventions, the boundaries of the labs. Create it next to the clone instead. uv init makes it a git repository of its own:

cd .. # the folder that holds ai-engineering-robotframework
uv init --lib demoshop-library
cd demoshop-library

Before the first real prompt, ask your agent which instruction files it loaded (in Claude Code, /memory lists them). If it names a file from outside the new project, move the project somewhere without one.

2. Five kinds of context​

Write them into the project's AGENTS.md before you ask for any code:

ContextWhat it saysExample
Toolstackhow the project is built, tested and packaged, and what it may depend onuv add, never pip; uv run pytest; uv build
Referencesthe specifications to build against, at the versions in use, as saved files where possiblethe service's OpenAPI description; a User Guide chapter for 7.5
Conceptsthe ideas the code must followAssertionEngine's operators; the listener interface, version 3
Examplescode or keywords to imitateBrowser's Get Text, saved from libdoc
Specificationwhere the agreed behaviour lives, and that work follows itOpenSpec: openspec/changes/<name>/

A skeleton:

# AGENTS.md

<One sentence: what this project is, and who uses it.>

## Toolstack
- Python 3.12, managed with uv: `uv add <package>` for every dependency, never pip.
- Unit tests: `uv run pytest`. Robot Framework tests: `uv run robot atest`.
- Package: `uv build`. Dependencies at run time: <the list>, pinned in pyproject.toml.

## References
- <the interface or API to build against: a saved file under references/>
- <the User Guide chapter for it, at Robot Framework 7.5>
- <the Robot API page for it, at Robot Framework 7.5>

## Concepts
- <what the code must follow, and where to read about it>

## Examples
- <keywords or code to imitate: saved files under references/>

## Specification
- Every change goes through OpenSpec, under openspec/changes/. Build only what the current change's tasks say.

Keep it short. It is loaded into every session, and the references it names are read only when needed.

3. Spec-driven from the first prompt​

Set OpenSpec up in the new project, for your agent:

openspec init --tools claude # or: codex, github-copilot

openspec/config.yaml then holds a commented example of context:. Uncomment it, and write the five kinds of context in a few lines each. Every proposal, spec, design and task list the agent writes then starts from them. Then propose one slice, for example "the catalogue keywords": /opsx:propose (Claude Code), $openspec-propose (Codex) or /opsx-propose (GitHub Copilot). It is the same flow as Lab 5.

4. Where Robot Framework documents what you build​

All links are for Robot Framework 7.5. A link without a version, such as latest, moves on to the next release, while your project stays on its pinned one.

You buildUser GuideRobot API
a keyword libraryCreating test libraries, library scope, hybrid library API, dynamic library API, Libdocthe keyword and library decorators
a listenerListener interface, version 3, examplesListenerV3, and the running and result models it receives
a pre-run modifierModifying executed suites and testsSuiteVisitor
a tool that reads resultsModifying resultsExecutionResult, ResultVisitor
a parser for test dataParser interfacethe robot.api page, parsing

Concepts other libraries share:

  • AssertionEngine: the assertion operators of Browser's Get keywords (Get Text h1 == Welcome), as a library your own keywords can use.
  • PythonLibCore: the structure Browser and others build their libraries on, with keywords spread over several classes.

Both are installed in the workshop's environment, as dependencies of Browser, so an agent can read their code there.

5. Save what the agent cannot read​

A link helps only if the agent can open it. Save every reference it needs into the project, under references/, and name the files in AGENTS.md:

  • An API's description: export it, at the version you build against. The workshop's shop is pinned, while the public DemoShop runs a newer development version:

    curl http://localhost:9090/openapi.json -o references/demoshop-openapi.json
  • The part you need of a large description: GitHub's REST description is about 10 MB. Keep the paths you use:

    uv run --no-project python -c "
    import json, urllib.request
    url = 'https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/dereferenced/api.github.com.deref.json'
    spec = json.load(urllib.request.urlopen(url))
    keep = ['/repos/{owner}/{repo}/issues', '/repos/{owner}/{repo}/issues/{issue_number}/comments']
    json.dump({path: spec['paths'][path] for path in keep}, open('references/github-issues.json', 'w'), indent=1)
    "
  • Keywords to imitate: save their documentation from a project where the library is installed, for example Browser's from the workshop clone:

    uv run robotcode libdoc Browser show "Get Text" > ../demoshop-library/references/browser-get-text.md
  • A manual that refuses agents: some answer automated requests with 403 Forbidden, as TestRail's API manual does. Open it in your browser and save the pages you need under references/, or export the service's API description if it offers one.

6. One slice at a time​

Propose a slice of three to five keywords, or one event of a listener. Read the proposal as you would review a colleague's: is every keyword named the way the examples are, does every assertion use the concepts you named? Then apply it, run the checks, and look at the result before you propose the next slice.