agentleFS
MarketSquareMarketSquare/robotframework-browser/.claude/skills/change-keyword/SKILL.md

change-keyword

Skill663 starsChanged 6 days agoApache-2.0View on GitHub

Add, change or remove a Browser library keyword across proto, Node and Python. Use when a task touches a keyword''s name, arguments, behaviour or docstring, or adds a new one.

The file

We split this skill into the rules it gives an agent

9 rules. About 0 are common, 0 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Every public keyword becomes an entry in each translation users maintain (rfbrowser translation), so removing or renaming one is a breaking …

    line 32
  2. Very specific:

    Add or change the rpc in service Playwright and its messages under Request or Response.

    line 41
  3. Very specific:

    Run inv node-build; it regenerates the Python and TypeScript stubs.

    line 45
  4. Very specific:

    Write the implementation as a function in the feature module (getters.ts, interaction.ts, ...) with the signature (request, state: …

    line 51
  5. Very specific:

    Put the method on the keyword class for its feature area in Browser/keywords/, decorated with @keyword(tags=(...)).

    line 63
  6. Very specific:

    Check the result the way users read it: python -m robot.libdoc Browser show "<Keyword Name>" prints the signature and docstring, and inv …

    line 88
  7. Very specific:

    Run this step without being asked, once step 6 is done.

    line 107
  8. Very specific:

    Deprecate: Start the docstring with *DEPRECATED* and name the replacement, as Open Browser does in playwright_state.py.

    line 124
  9. Very specific:

    Remove in the major release: Delete the keyword across all three layers and its acceptance tests, and say in the release notes which …

    line 133

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Change a keyword

A keyword spans three layers: a Python method in Browser/keywords/, an rpc in protobuf/playwright.proto, and a Node handler in node/playwright-wrapper/. Copy the example that matches the keyword:

  • Takes a selector, as most keywords do: Get Text. Getters.get_text → rpc GetText → getText = this.wrapping(getters.getText) in grpc-service.ts → getters.getText.
  • No selector: Get Title. Getters.get_title → rpc GetTitle → getTitle in grpc-service.ts → getters.getTitle.

Steps

1. Is there a need for a keyword?

For a new keyword, name the need first. It is one of:

  • Playwright functionality that can only be exposed to users as a keyword, or
  • a user need that is generally useful, not one test's convenience.

Then look for a way that keeps the keyword count down: a new argument on an existing keyword, a better assertion, or one higher-level keyword that combines several Playwright calls. State that plugins, libraries built on Browser, or acceptance-test helpers need goes on an internal Browser property, not a keyword (ADR 0009).

Every public keyword becomes an entry in each translation users maintain (rfbrowser translation), so removing or renaming one is a breaking change for them too. Removal and renaming follow their own path: see Removing or renaming a keyword.

Done when the need is stated in one sentence and the maintainer agrees with the shape: new keyword, new argument, or no keyword.

2. Proto

Add or change the rpc in service Playwright and its messages under Request or Response. Reuse an existing message when one fits (Request.Empty, Response.String, Response.Json). Arguments go in a typed Request message, not Request.Json, one rpc per Playwright call (ADR 0013). Run inv node-build; it regenerates the Python and TypeScript stubs.

Done when inv node-build succeeds.

3. Node

Write the implementation as a function in the feature module (getters.ts, interaction.ts, ...) with the signature (request, state: PlaywrightState) that returns a pb.Response_*. Register it in grpc-service.ts as name = this.wrapping(module.name). wrapping resolves the caller's state, logs the call, and turns errors into errorResponse. A selector keyword resolves its element with findLocator(state, request.selector, request.strict, ...) from playwright-invoke.ts, which handles frame piercing. Strict mode only applies because Python sends strict in the request (step 4).

Done when inv node-build and inv lint-node pass.

4. Python

Put the method on the keyword class for its feature area in Browser/keywords/, decorated with @keyword(tags=(...)); a new class must also be added to the libraries list in Browser.__init__. Call Node with with self.playwright.grpc_channel() as stub:.

  • Annotations stay narrow: an Enum, not Enum | str (ADR 0006).
  • A selector keyword starts with selector = self.presenter_mode(selector, self.strict_mode) and sends strict=self.strict_mode in its request, the way get_text does. presenter_mode also applies the selector prefix (Set Selector Prefix), so skipping it breaks more than presenter mode.
  • A getter asserts with @with_assertion_polling and the assertionengine helpers (verify_assertion and friends), the way get_title and get_text do.
  • A timeout: timedelta | None = None argument goes to Node as self.get_timeout(timeout): milliseconds, falling back to the library timeout when the caller gave none.
  • Other settings come from LibraryComponent properties (self.strict_mode, self.timeout), never from scope_stack directly (ADR 0009).

Done when inv lint-python passes.

5. Docstring

The docstring is the user documentation. It says what the keyword is for, what each argument really does, and gives an example when usage is not obvious. When an argument's behaviour changes, rewrite its row and check the example still holds. Code and tests stay free of explanatory comments. inv node-build regenerates browser.pyi from the new signature.

Check the result the way users read it: python -m robot.libdoc Browser show "<Keyword Name>" prints the signature and docstring, and inv docs renders the full page to docs/Browser.html (gitignored) for checking links and formatting.

Done when every argument is described, the rendered page shows no broken links or formatting, and utest/test_docs.py passes.

6. Tests

  • Jest for Node logic worth testing in isolation (node-unit-test skill).
  • pytest for Python logic that runs without a browser (python-unit-test skill).
  • Acceptance tests for the keyword's behaviour against the Test App (write-robot-tests skill). A new page or element for them goes in node/dynamic-test-app/.

Done when inv utest, inv utest-node and the affected inv atest --suite runs pass, and inv lint is clean.

7. Review

Run this step without being asked, once step 6 is done. The review reads commits, so commit the work on its branch first. Then dispatch a subagent to run the mattpocock-skills:code-review skill, with the branch's merge-base with main as the fixed point.

The subagent sees only its prompt, so give it the spec: the issue, plus every decision the maintainer made that overrides or adds to it, such as a dropped argument.

Relay both axes to the maintainer, and treat each finding as theirs to accept or reject. Check a finding against the code before relaying it. Apply the accepted ones, then repeat step 6.

Done when the maintainer has ruled on every finding and step 6 passes with the accepted fixes in.

Removing or renaming a keyword

A keyword is never removed in the release that deprecates it. It is deprecated first, and removed only in the next major release (Browser/version.py has the current version).

  1. Deprecate. Start the docstring with *DEPRECATED* and name the replacement, as Open Browser does in playwright_state.py. Robot Framework then warns on every call. A renamed keyword keeps working under its old name until the removal.
  2. Offer an automatic conversion when one is possible. rfbrowser transform rewrites users' test data with a robocop formatter. Browser/robocop_transformer/network_idle.py converts Wait Until Network Is Idle to Wait For Load State networkidle and is the example to copy. A new conversion needs the formatter, a flag on transform in Browser/entry/__main__.py, wiring in Browser/entry/transform.py, and a test in atest/test/11_tidy_transformer/. Tell the maintainer when the change cannot be converted mechanically.
  3. Remove in the major release. Delete the keyword across all three layers and its acceptance tests, and say in the release notes which keyword or rfbrowser transform flag replaces it.

Done when the keyword is deprecated with its replacement named, the maintainer has decided whether rfbrowser transform gets a conversion, and the removal is planned for the next major release.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.