From 432597a9c802f4f8205548f984ed93bb4463858b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 07:34:15 +0000 Subject: [PATCH 01/13] Add docs maintenance agent: AGENTS.md, skills, and rule files Adds the orchestrator (AGENTS.md) and seven self-contained skill files under .agents/skills/ for linting, accuracy verification, fresh-user evaluation, gap analysis, translation, corrections capture, and screenshot triage. Seeds glossary.yml with product terms that must never be translated and spelling variants lint should flag. Adds empty style-exceptions.yml and translation-rules.yml with schema comments. Stops ignoring /i18n so machine translations can be committed alongside the English source, per the translation architecture in AGENTS.md. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/accuracy-check.md | 216 +++++++++++++++++++++ .agents/skills/corrections-capture.md | 179 ++++++++++++++++++ .agents/skills/fresh-user-eval.md | 143 ++++++++++++++ .agents/skills/gap-analysis.md | 187 ++++++++++++++++++ .agents/skills/lint.md | 254 +++++++++++++++++++++++++ .agents/skills/screenshot-triage.md | 200 ++++++++++++++++++++ .agents/skills/translate.md | 262 ++++++++++++++++++++++++++ .gitignore | 3 +- AGENTS.md | 230 ++++++++++++++++++++++ glossary.yml | 194 +++++++++++++++++++ style-exceptions.yml | 44 +++++ translation-rules.yml | 59 ++++++ 12 files changed, 1969 insertions(+), 2 deletions(-) create mode 100644 .agents/skills/accuracy-check.md create mode 100644 .agents/skills/corrections-capture.md create mode 100644 .agents/skills/fresh-user-eval.md create mode 100644 .agents/skills/gap-analysis.md create mode 100644 .agents/skills/lint.md create mode 100644 .agents/skills/screenshot-triage.md create mode 100644 .agents/skills/translate.md create mode 100644 AGENTS.md create mode 100644 glossary.yml create mode 100644 style-exceptions.yml create mode 100644 translation-rules.yml diff --git a/.agents/skills/accuracy-check.md b/.agents/skills/accuracy-check.md new file mode 100644 index 000000000000..247941a75b5f --- /dev/null +++ b/.agents/skills/accuracy-check.md @@ -0,0 +1,216 @@ +# Skill: Accuracy verification + +Extract every verifiable claim from a docs page and check it against the code +that implements it. The docs describe three products in three repos; you must +look in the right one. + +## Inputs + +- The page (markdown source) or a section of pages. +- Read access to the product repos. Clone them into a scratch directory, not + into this repo: + + ```bash + SCRATCH=${SCRATCH:-/tmp/openfn-src} + mkdir -p "$SCRATCH" + git clone --depth 50 https://github.com/OpenFn/lightning "$SCRATCH/lightning" + git clone --depth 50 https://github.com/OpenFn/kit "$SCRATCH/kit" + git clone --depth 50 https://github.com/OpenFn/adaptors "$SCRATCH/adaptors" + ``` + + Check out the tag that matches what the docs describe when the page names a + version. Otherwise use the default branch, and say so in the output. + +## Where to look + +| Claim is about | Repo | Start here | +| ----------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------- | +| Web app UI, triggers, credentials, projects, runs, history, permissions, limits, API endpoints | `OpenFn/lightning` | `lib/lightning_web/router.ex` for routes; `lib/lightning_web/live/` for pages; `lib/lightning/` for domain logic; `config/runtime.exs` for env vars; `priv/repo/migrations/` for schema | +| Webhook auth, provisioning API, workflows API | `OpenFn/lightning` | `lib/lightning_web/controllers/api/` (`provisioning_controller.ex`, `workflows_controller.ex`, `run_controller.ex`, ...), `lib/lightning_web/controllers/webhooks_controller.ex`, `lib/lightning/workflows/webhook_auth_method.ex` | +| CLI commands and flags | `OpenFn/kit` | `packages/cli/src/commands.ts` (command list), `packages/cli/src//` (one dir per command: `deploy`, `execute`, `pull`, `docs`, `collections`, `projects`, ...), `packages/cli/src/options.ts`, `packages/cli/README.md` | +| `openfn deploy`, `project.yaml`, project spec format | `OpenFn/kit` | `packages/deploy/src/`, `packages/project/src/` | +| Job syntax, `state`, `$` lazy operator, `fn()`, `each()`, cursors, compilation | `OpenFn/kit` | `packages/compiler/`, `packages/runtime/`; common operations are in `OpenFn/adaptors` at `packages/common/src/` | +| Adaptor functions, configuration schema, versions | `OpenFn/adaptors` | `packages//src/Adaptor.js` (JSDoc), `packages//configuration-schema.json`, `packages//CHANGELOG.md`, `packages//package.json` | +| Docs site itself (build commands, contributing) | this repo | `package.json`, `README.md`, `.github/workflows/` | + +If a claim is about hosted-plan pricing, limits that are set per deployment, +roadmap, or policy, it is not verifiable from code. Mark it **uncertain** and +name Brandon's team (product) as the owner in the output. Do not guess. + +## Process + +### 1. Extract claims + +Read the page once and list every statement a reader could act on and be +wrong about. Number them. Categories: + +- **Code sample**: any fenced block the reader is meant to run or copy. +- **API behaviour**: a function, operation, endpoint, event, or command exists + and behaves as described (signature, arguments, defaults, return shape, + error behaviour). +- **UI behaviour**: a button, menu, page, or setting exists with the stated + name and does the stated thing. +- **Configuration**: an option, env var, front matter key, `project.yaml` + field, or credential schema key exists with the stated name, type, default, + and constraints. +- **Version**: a version number, "since v2.x", "as of", "latest", or a + compatibility statement. +- **Path or URL**: a file path, route, or link that must exist. +- **Quantity**: a limit, timeout, size, retention period, count. + +Skip marketing sentences, motivation, and analogies. Skip claims inside +`:::note In OpenFn V1 ...` history callouts unless the page is a migration +page. + +### 2. Check each claim + +For each claim, find the implementing code and record `repo/path:line`. + +**Code samples** + +- Job expressions (JavaScript using adaptor operations): run them with the + CLI against a stub state to confirm they compile and the named operations + exist. + + ```bash + cd "$SCRATCH" && npm i -g @openfn/cli + echo '{"configuration":{}, "data":{}}' > state.json + openfn compile sample.js -a # must compile + openfn sample.js -a -s state.json # runs if no network call + ``` + + Where a sample needs a live system, stop at `compile` and check each + operation's signature in the adaptor JSDoc. Confirm the described output + matches the return shape in the code. + +- Shell commands: run them where safe (`openfn --help`, `openfn --help`) + and compare flags to the page. +- JSON/YAML config: validate against the schema (`configuration-schema.json` + for credentials; the `project.yaml` schema in `packages/deploy` for + projects). +- Do not run anything that deploys, deletes, sends email, or contacts a live + OpenFn instance. + +**API and UI behaviour** + +- Grep for the exact name the docs use. If it is not found, grep for + synonyms and look at recent commits (`git log -S ''`) to see whether + it was renamed. A rename is a **fix** if the new name is unambiguous; + otherwise a **question**. +- For Lightning UI text, search `priv/gettext/` and `lib/lightning_web/` for + the label string. Button labels and menu names in the docs must match the + code exactly, including capitalisation. +- For routes, confirm in `router.ex`. + +**Configuration options** + +- Env vars: `grep -rn "System.get_env(\"NAME\"" config/ lib/` in Lightning, + or `process.env.NAME` in kit. Compare defaults. +- Confirm every option listed in a table exists, and note options that exist + in code but are missing from the table (report those to the gap analysis + skill, not as accuracy failures). + +**Versions** + +- Compare against `package.json` (kit, adaptors) or `mix.exs` (Lightning) on + the default branch, and against the latest git tag. A doc that says + "latest" and gives a stale number is a **fix** only if the page is about + installing that version; otherwise a **suggestion** to remove the number. + +**Quantities** + +- Find the constant or config value. Values that are set per deployment + (rate limits, retention, payload size) are **uncertain** unless the docs + page is explicitly about the hosted app and the value is in + `config/runtime.exs` defaults. + +### 3. Classify + +- **pass**: code matches the docs. +- **fail**: code contradicts the docs. Record expected (docs) vs actual + (code, with path and line). +- **uncertain**: could not find the implementing code, or the behaviour is + deployment-specific, or requires a live system to verify. + +Then, for each **fail**, decide the action: + +- **fix** when the correct value is in the code and the surrounding sentence + still makes sense after the change (a flag name, a default, a menu label, a + version, a path). +- **suggestion** when fixing requires rewriting a paragraph or the docs might + be describing intended-but-unshipped behaviour. +- **question** when the docs and code disagree and either could be the bug. + Say which you suspect and why. + +## Generated adaptor reference pages + +Pages under `adaptors/packages/` (`-docs`, `-configuration-schema`, +`-changelog`, `-readme`) are rendered at build time from JSDoc in +`OpenFn/adaptors`. Never edit them here. + +When a claim on a generated page fails: + +1. Locate the JSDoc block in `$SCRATCH/adaptors/packages//src/` (usually + `Adaptor.js`). The function name in the page heading is the JSDoc + `@function` or export name. Configuration pages come from + `packages//configuration-schema.json`. +2. Draft an issue body in this exact shape and put it in the output under + "Upstream issues": + + ```markdown + ## adaptor: docs for `()` do not match behaviour + + **Page:** https://docs.openfn.org/adaptors/packages/-docs# + **Source:** `packages//src/Adaptor.js` L (JSDoc for ``) + **Adaptor version:** /package.json> + + ### What the docs say + + + + ### What the code does + + + + ### Suggested JSDoc change + + ```js + /** + * + */ + ``` + + Found by the docs accuracy check while reviewing . + ``` + +3. Also check whether a hand-written overview page `adaptors/.md` + repeats the same wrong claim. If it does, that copy is editable here and + is a normal **fix**. + +Do not file the issue unless the user has asked you to file issues. Draft +only. + +## Output + +``` +Page: docs/.md +Repo(s) checked: lightning@, kit@, adaptors@ +Claims: N checked. Pass: N. Fail: N. Uncertain: N. + +| # | Claim (short) | Category | Result | Evidence | +|---|---------------|----------|--------|----------| +| 1 | `openfn deploy` reads `project.yaml` by default | config | pass | kit/packages/deploy/src/index.ts:42 | +| 2 | Retention default is 7 days | quantity | uncertain | deployment-specific; config/runtime.exs:210 has no default | + +Failures: +[fix] docs/.md:L — docs say ""; code does "" (repo/path:line) — changed to "" +[suggestion] ... +[question] ... + +Upstream issues (OpenFn/adaptors): + +``` + +Apply the fixes, run Prettier on changed files, and confirm `yarn build` +still passes. Hand suggestions, questions, and upstream drafts to the PR +description. diff --git a/.agents/skills/corrections-capture.md b/.agents/skills/corrections-capture.md new file mode 100644 index 000000000000..a8ef8515dadd --- /dev/null +++ b/.agents/skills/corrections-capture.md @@ -0,0 +1,179 @@ +# Skill: Corrections capture + +When a human overrides something the agent produced, turn the override into a +rule so the same correction never has to be made twice. Capture the general +pattern, not the specific fix. + +## Triggers + +Run this skill when any of these happen: + +| Trigger | Where you see it | Likely rule file | +| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------- | +| A human edits a machine-translated page | A commit or PR touching `i18n/**` by a human author, or a review comment changing translated text | `glossary.yml` or `translation-rules.yml` | +| A human sets `translation_review_status: human-reviewed` | Front matter change in `i18n/**` | `translation-rules.yml` (mine the diff between machine and reviewed versions) | +| A human reverts or rejects a lint fix | A review comment on a lint commit, a follow-up commit undoing it, or a "won't fix" on a suggestion | `style-exceptions.yml` | +| A human rewrites a section the fresh-user eval flagged | A commit changing lines the eval reported, in a way different from the eval's proposal | `style-exceptions.yml` (if the eval's pattern should not be flagged) or a note in the PR | +| A human rejects an accuracy fix | Revert or review comment | Usually a **question** for the product owner, not a rule | + +## Inputs + +- The agent's original output: the commit, PR diff, or finding text. +- The human's override: the later commit, the review comment, or the edited + file. +- The three rule files at the repo root. + +Find candidates with: + +```bash +# Human commits to translated pages since the last agent commit +git log --format='%H %an %s' --no-merges -- i18n/ | grep -v -i 'docs-agent\|translate(' | head + +# Reverts of agent commits +git log --format='%H %s' --grep='Revert' --grep='revert' -i | head + +# PR review threads: use the GitHub tools to list review comments on the +# agent's PRs and filter for "changes requested" or comments on lines the +# agent changed. +``` + +## Process + +### 1. Pair the agent output with the human edit + +For each candidate, produce a minimal before/after: + +```bash +git diff -- +``` + +Discard pairs where the human change is unrelated to what the agent did +(a new paragraph, an unrelated typo fix). + +### 2. Extract the general rule + +Ask, in order: + +1. **Is it terminology?** The human replaced one word or short phrase with + another, and the same replacement would apply anywhere the phrase occurs. + - In an English page: add or extend a `glossary.yml` entry. If the human + restored a spelling the lint changed, the lint's target was wrong; + correct `variants` and `term` accordingly. + - In a translated page where the human restored an English term: add the + term to `glossary.yml` with `translate: false`. + - In a translated page where the human changed how a phrase is rendered + and the phrase is not a product term: add a `translation-rules.yml` + entry with `kind: term`. + +2. **Is it a translation pattern?** The change is about register (tú/usted, + vous/tu), punctuation, how UI labels are rendered, how admonition titles + are handled, sentence structure, or a rendering to avoid. Add a + `translation-rules.yml` entry with the matching `kind`. Write the + `instruction` so a translator who has never seen the example would apply + it correctly. + +3. **Is it a rejected lint finding?** The human undid a lint change or said + "no" to a suggestion. Add a `style-exceptions.yml` entry. Scope it as + narrowly as the evidence supports: one page if the rejection was about + that page's content, a directory if the reviewer said "we do this + throughout X", the whole repo only if they said so. + +4. **Is it a style or phrasing preference** that is not a lint rule (the + human prefers "select" to "click", or wants imperative headings)? Add it + to `style-exceptions.yml` under the rule id `terminology` or + `heading-case` with a `match`, and also record the preferred form in + `glossary.yml` if it is a word-level preference. If it fits neither + schema, add a `# NOTE:` comment at the top of `style-exceptions.yml` + describing the preference and open a **question** to extend the schema. + +5. **Is it a disagreement about facts** (the human reverted an accuracy + fix)? Do not write a rule. Record a **question** in the PR: "Accuracy + check found X at repo/path:line, reviewer restored Y. Which is right?" + Product questions go to Brandon's team. + +A single human edit can yield more than one rule. A single rule should cover +every future instance of the pattern, so prefer "UI button labels match the +French UI strings in OpenFn/lightning" over "Save and Run → Enregistrer et +exécuter" alone. Record the specific example in `example_source` and +`example_target` and the general rule in `instruction`. + +### 3. Write the rule + +Append to the relevant file following its header schema. Always fill in: + +- `reason`: one sentence, in your words, of why the human made the change. + If the human left a review comment, quote it. +- `added_by`: the human's GitHub handle (the person whose edit you are + capturing, not you). +- `added_on`: today's date, ISO format. +- `source_pr`: the PR or commit where the override happened. + +Do not add PII beyond a GitHub handle. Do not paste support-ticket text. + +Check the file still parses: + +```bash +node -e 'const y=require("js-yaml"); for (const f of ["glossary.yml","style-exceptions.yml","translation-rules.yml"]) y.load(require("fs").readFileSync(f,"utf8")); console.log("ok")' +``` + +(`js-yaml` ships with Docusaurus, so it is in `node_modules`.) + +### 4. Check for conflicts + +- A new `glossary.yml` entry must not duplicate an existing `term` or + appear in another entry's `variants`. Merge instead. +- A new `style-exceptions.yml` entry must not silence a rule so broadly that + the lint stops working (`rule: internal-link`, `scope: "**"`, no `match` + is never acceptable). If the human asked for that, record it as a + **question**. +- A new `translation-rules.yml` entry must not contradict an existing rule + for the same locale and `source`. If it does, keep the newer one and + record the conflict in the PR for the reviewer to confirm. + +### 5. Apply retroactively where cheap + +- New glossary term: grep `docs/` for variants and fix them (that is a normal + lint fix). Grep `i18n/` for translated occurrences of a now-protected term + and list them under "Also touched" or as a follow-up if the count is + large. +- New style exception: nothing to apply. +- New translation rule: do not retranslate other pages now. The next + translate run will pick up the rule. + +### 6. Open a PR + +One PR per capture run, titled `rules: capture corrections from `. Body: + +```markdown +## What was overridden + +- changed L: "" +- changed it to: "" () + +## Rules added + +- glossary.yml: `` ... +- translation-rules.yml: "" +- style-exceptions.yml: on + +## Retroactive changes + +- + +## Questions + +- +``` + +Tick "I have used Claude Code" in the AI Usage section of the PR template. +Rules take effect only after this PR is merged; do not rely on them in the +same run. + +## Do not + +- Do not capture a one-off (a typo the human fixed, a rewording specific to + one sentence) as a rule. If you cannot state the rule in a form that + would apply to at least one other page, skip it. +- Do not edit the human's change itself. +- Do not mark anything `human-reviewed`. Only humans set that field. diff --git a/.agents/skills/fresh-user-eval.md b/.agents/skills/fresh-user-eval.md new file mode 100644 index 000000000000..cd4bc26726b4 --- /dev/null +++ b/.agents/skills/fresh-user-eval.md @@ -0,0 +1,143 @@ +# Skill: Fresh-user evaluation + +Read a docs page the way a new user would, with no prior knowledge of OpenFn, +and try to do what the page says. Report every place you had to guess. + +This skill is about the reader's experience, not correctness. Correctness is +the accuracy-check skill (`.agents/skills/accuracy-check.md`). Run this skill +after accuracy so you are evaluating a page whose claims are already known +to be true. + +## Inputs + +- One docs page (markdown source). Evaluate one page at a time even when the + section has many. +- Read access to the relevant product repo(s) for verifying that what you + eventually figured out is actually right. Use the repo map in + `.agents/skills/accuracy-check.md` to find the right one. +- Nothing else. In particular, do not read neighbouring pages during the + first pass. + +## Process + +### Pass 1: read cold + +1. Clear your assumptions. You know what a webhook, an API, JSON, and a + terminal are. You do not know what a Step, a work order, a dataclip, an + adaptor, or the Canvas is unless this page tells you. +2. Read the page top to bottom once, without following any links. +3. Write down, in one sentence, what task or concept the page is teaching. + If you cannot, that is your first finding. +4. Write down who the page seems to be for (non-technical project manager, + implementer building a workflow in the web app, developer using the CLI, + self-hoster). If the page switches audience halfway, note where. + +### Pass 2: attempt the task + +Follow the page as literally as you can. + +- For procedural pages ("Configure a Step", "Deploy with the CLI"): perform + each step. Where it needs the web app, walk the route in the Lightning + source (`lib/lightning_web/live/`) or use a local instance if one is + available, and confirm the named button or menu exists where the page says. + Where it needs the CLI, run the commands. +- For conceptual pages ("State", "Key Concepts"): after reading, try to + explain the concept back in two sentences and then answer three questions a + new user would ask. If you cannot answer them from the page, record what is + missing. +- For reference pages (tables of options, status codes): pick three entries + and check you could use each one from the description alone. + +At every point where you had to stop, record a finding. Triggers: + +- **Guess**: the page uses a term, path, or name it never defined, and you + had to infer it. Record the term and what you inferred. +- **Stuck**: the next step depends on something the page did not tell you + (where a button is, what value to enter, which page to be on first). +- **Ambiguity**: a sentence has two readings and they lead to different + actions. +- **Missing prerequisite**: you needed an account, a credential, an installed + tool, or an earlier setup step the page assumes. +- **Order**: the steps are listed in an order that does not work if followed + literally. +- **Unverifiable outcome**: the page tells you to do something but not what + success looks like. +- **Dead end**: a link you needed to follow to continue (after Pass 1 you may + follow links) went to a page that does not answer the question. + +### Pass 3: verify your guesses + +For each Guess and Ambiguity, check the code or a neighbouring page to learn +the right answer. Record whether your guess was right. A wrong guess is +strong evidence the page needs the information; a right guess is weaker but +still a finding. + +## Classify findings + +- **fix**: the missing information is a single fact you have now verified + (the menu path, the default value, the prerequisite command), and it fits in + one sentence or one list item at a specific line. Apply it. +- **suggestion**: the page needs restructuring, a new subsection, an example, + a screenshot, or a rewrite of more than a couple of sentences. Propose the + text in the PR description. Do not apply. +- **question**: you could not determine the right answer from code, or the + fix depends on which audience the page is for. Ask. + +Do not "fix" tone or voice. Do not add content beyond what the finding needs. + +## Scores + +Give two scores after the findings. Whole numbers only. + +**Readability (1 to 5)**: how easy the prose was to follow on the first pass. + +- 5: read it once, understood everything, no re-reading. +- 4: one or two sentences needed re-reading; terminology mostly defined. +- 3: understood the gist but several undefined terms or long detours. +- 2: had to reconstruct the meaning from context repeatedly. +- 1: could not tell what the page was about without outside knowledge. + +**Completeness (1 to 5)**: could a new user accomplish the task with only +this page? + +- 5: yes, start to finish, including knowing when they are done. +- 4: yes, with one small guess that turned out right. +- 3: yes, but only after following links or guessing more than once. +- 2: no, a required step, prerequisite, or value is missing. +- 1: no, the page does not actually describe how to do the task. + +Justify each score in one sentence that names the specific thing that cost +points. + +## Output + +``` +Page: docs/.md +Teaches: +Audience: ; switches at L if applicable +Attempted: + +Findings: +[fix] docs/.md:L — guess — "" never defined; inferred ""; verified at — added "" +[suggestion] docs/.md:L — stuck — step 4 says "select the credential" but no credential exists yet — propose inserting a "Before you begin" list: ... +[question] docs/.md:L — ambiguity — "the run" could mean the Run or the manual run button — which? + +Readability: N/5 — +Completeness: N/5 — +``` + +Apply fixes, run Prettier on changed files, and confirm `yarn build` passes. +Put scores in the PR "Scores" table, suggestions and questions in their +sections. + +## Do not + +- Do not read the page's git history or the PR that introduced it before + Pass 1. That is prior context a user would not have. +- Do not evaluate generated adaptor reference pages (`adaptors/packages/**`). + Their readability is a JSDoc concern; if a hand-written overview page + (`adaptors/.md`) exists, evaluate that instead. +- Do not edit pages with `translation_review_status: human-reviewed`. Record + suggested diffs. +- Do not write a new page. If the task cannot be done because the page does + not exist, that is a finding for the gap analysis skill. diff --git a/.agents/skills/gap-analysis.md b/.agents/skills/gap-analysis.md new file mode 100644 index 000000000000..0eb1b3d8d8aa --- /dev/null +++ b/.agents/skills/gap-analysis.md @@ -0,0 +1,187 @@ +# Skill: Gap analysis + +Compare what a docs section covers against what exists in the product and +what users ask about. Produce a ranked list of gaps. Do not write the missing +pages unless the user asked for them. + +## Inputs + +- A section: a sidebar category from `sidebars-main.js` or a directory under + `docs/`. +- The product repos, cloned to a scratch directory (see the setup block in + `.agents/skills/accuracy-check.md`). +- Optional, only if the session has access: support channels and search + analytics (see "Optional sources"). Never assume access; check, and say in + the output which sources you used. + +## Process + +### 1. Inventory what the docs cover + +For every page in the section, list the concepts, features, commands, +options, and endpoints it documents. Use the page's headings plus any tables. +Write this as a flat list of "covered items", each with the page and heading +where it lives. + +```bash +grep -n -E '^#{2,4} ' docs/
/*.md +``` + +Also list every internal link the section makes to pages outside the section. +Those are things the section assumes are documented elsewhere; check that +they actually are. + +### 2. Inventory what exists in the product + +Pick the inventory that matches the section. Do only the relevant ones. + +**Web app features (Platform sections)**: in `$SCRATCH/lightning` + +```bash +# routes = user-facing pages and API endpoints +grep -n -E 'live |get |post |put |patch |delete ' lib/lightning_web/router.ex +# LiveView modules = screens +ls lib/lightning_web/live/ +# feature flags and config toggles +grep -rn -E 'Application\.(get_env|fetch_env)' lib/ | grep -o -E ':[a-z_]+\]?' | sort -u +grep -n -E 'env!?\(' config/runtime.exs +``` + +**CLI (CLI section)**: in `$SCRATCH/kit` + +```bash +grep -n -E "^\s+'?[a-z-]+'?" packages/cli/src/commands.ts | head -60 # command names +ls -d packages/cli/src/*/ # one dir per command +grep -rn -E "^\s+'?[a-z-]+'?:\s*\{" packages/cli/src/options.ts | head -100 +npx @openfn/cli --help +``` + +**Job writing (Write Jobs section)**: in `$SCRATCH/adaptors/packages/common/src/` +for every exported operation, and `$SCRATCH/kit/packages/compiler/` for +syntax transformations (lazy `$` operator, `fn` wrapping, imports). + +**Deployment (Deployment section)**: `$SCRATCH/lightning/DEPLOYMENT.md`, +`config/runtime.exs` env vars, `docker-compose.yml`, and the Helm or +Kubernetes manifests if present. + +**Adaptors (Adaptors section)**: every `packages/` in +`$SCRATCH/adaptors` should have a generated reference page. Hand-written +overview pages (`adaptors/.md` in this repo) are optional; note which +of the twenty most downloaded adaptors (by `npm view @openfn/language-` +or recent changelog activity) lack one. + +**Migration**: compare `docs/migration/` against the v1 to v2 rename list in +`docs/get-started/terminology.md` and the migration tooling in `kit`. + +### 3. Diff + +For each product item with no covered item that matches: + +- Check the whole `docs/` tree, `articles/`, and `adaptors/*.md` before + declaring a gap. A feature documented in another section is a + cross-linking gap ("page exists but this section does not point to it"), + not a missing page. +- Distinguish: + - **missing page**: nothing in the docs mentions the item. + - **partial page**: a page exists and is the right home, but does not + cover this item (a flag, an option, an edge case, an error). + - **misplaced**: the item is documented, but in a section a user on this + task would not look in. + - **stale**: the item is documented for a previous version and the current + behaviour is different (hand this to the accuracy check if it is not + already recorded there). + +Also record the inverse: covered items that no longer exist in the product. +Those are accuracy failures; note them and move on. + +### 4. Optional sources + +Use these only if the tools are present in the session and the user has +connected them. Never fabricate examples of user questions. + +- **Community forum** (https://community.openfn.org): if you have web access, + search the last twelve months for the section's key terms. Count threads + per topic. A topic with three or more threads and no docs page is a + high-impact gap. +- **Support inbox or Slack** (if a connector is present): search the same + terms. Record the count, never the content or names of people asking. +- **Search analytics** (Algolia dashboard for index `openfn`, if + accessible): queries with zero results or with high volume and low + click-through that contain the section's terms. +- **GitHub issues** on `OpenFn/docs` labelled as documentation requests: + always available via the GitHub tools. Search for the section's terms. + +If none of these are accessible, say so and rank on product evidence alone. + +### 5. Rank by user impact + +Score each gap 1 to 5 on each of: + +- **Reach**: how many users hit this. Core workflow features and the + getting-started path are 5; niche self-hosting flags are 1 or 2. +- **Severity**: what happens without the doc. Silent data loss or a security + misconfiguration is 5; slight inconvenience is 1. +- **Evidence**: 5 if support or forum data shows repeated asks; 3 if the + feature is prominent in the UI or CLI help; 1 if you only found it in code. +- **Effort**: inverted. 5 if a paragraph fixes it; 1 if a whole tutorial is + needed. + +Impact = Reach + Severity + Evidence + Effort. Sort descending. Ties go to +the one with higher Severity. + +## Output + +``` +Section: , N pages, M covered items +Product inventory: lightning@ (R routes, L live views), kit@ (C commands), ... +Sources used: product code; GitHub issues (N matched); forum (not accessible) ... + +Gaps (ranked): + +1. [missing page] — impact 17/20 (reach 5, severity 4, evidence 4, effort 4) + What's missing: <two sentences> + Evidence: <repo/path:line>; <forum thread count or issue link> + Where it should live: docs/<dir>/<slug>.md, sidebar "<Category>" after "<existing page>" + Suggested outline: + - <H2> + - <H3> + - <H2> + +2. [partial page] docs/<path>.md does not cover <item> — impact 14/20 (...) + What's missing: ... + Evidence: ... + Where it should live: docs/<path>.md, new "## <heading>" after "## <existing heading>" + Suggested outline: ... + +3. [misplaced] ... + Where it is: docs/<path>.md#<anchor> + Where this section should link from: docs/<path>.md L<line> + +Covered items that no longer exist in the product: +- docs/<path>.md L<line>: <item> (removed in <repo> at <commit>) → handed to accuracy check +``` + +Write the full list into the PR under "Gaps (ranked)". Cap the PR list at the +top ten and attach the rest as a collapsed `<details>` block. + +## Actions you may take + +- **fix**: adding a cross-link to an existing page from the obvious place in + this section, when the target page clearly covers the item. One sentence, + one link. +- Everything else is a **suggestion**. Do not create pages, sections, or + sidebar entries unless the user asked for the gap to be filled. If they + did, write the page following the suggested outline, add it to + `sidebars-main.js`, and run `yarn build`. + +## Do not + +- Do not count generated adaptor reference pages as gaps in this repo. A + missing or thin adaptor function description is a JSDoc gap; note it as a + draft issue for `OpenFn/adaptors` under "Upstream issues". +- Do not invent user demand. If you have no support or analytics data, the + Evidence score maxes at 3. +- Do not propose documenting internal, experimental, or feature-flagged + behaviour. Check for a feature flag or `experimental` marker in the code + before listing a feature; if present, list it as a **question** ("Is X + meant to be public yet?") for Brandon's team rather than a gap. diff --git a/.agents/skills/lint.md b/.agents/skills/lint.md new file mode 100644 index 000000000000..c698893d1b2c --- /dev/null +++ b/.agents/skills/lint.md @@ -0,0 +1,254 @@ +# Skill: Style and structure lint + +Deterministic checks on docs markdown. Most of this needs no judgement: run +the check, apply the fix, move on. Use this skill first on any section, before +accuracy, fresh-user, or translation work. + +## Inputs + +- A section: a sidebar category from `sidebars-main.js`, a directory under + `docs/`, or a single page. +- `glossary.yml` at the repo root (terminology rules). +- `style-exceptions.yml` at the repo root (findings humans have rejected). +- `sidebars-main.js` and `sidebars-adaptors.js` (for orphan detection). +- `docusaurus.config.js` (for redirects and the `onBrokenLinks: 'throw'` + setting). + +## Files you must not lint-fix + +- `adaptors/packages/**` and `adaptors/library/jobs/auto/**`: generated at + build time from `OpenFn/adaptors`. Not in git. Any issue there is an + upstream JSDoc issue; record it and move on. +- `versioned_docs/**`: frozen v1 docs. Record findings as suggestions only. +- Any page whose front matter contains + `translation_review_status: human-reviewed`. Record findings as a suggested + diff only. + +## Setup + +```bash +yarn install --immutable +``` + +Then, for each check, work from a file list: + +```bash +# Example: the "Write Jobs" section +FILES=$(node -e ' + const s = require("./sidebars-main.js").docs; + const walk = (items, label) => items.flatMap(i => + typeof i === "string" ? (label ? [i] : []) : + i.type === "category" ? walk(i.items, label || i.label === process.argv[1]) : []); + console.log(walk(s, false).map(id => `docs/${id}.md`).join("\n")); +' "Write Jobs") +``` + +Adjust the extension to `.mdx` where the file is MDX. If a sidebar id points to +a file that does not exist with either extension, that is a **fix**: correct +the id or restore the file. + +## Checks + +Run every check on every file in the section. Load `style-exceptions.yml` +first and drop any finding that matches an exception (`rule` equal, file +matches `scope` glob, and `match` substring or regex present in the finding +text when `match` is set). + +### 1. Terminology (`terminology`) + +For each entry in `glossary.yml`: + +- For every string in `variants`, search the prose (not code blocks, not + inline code, not URLs, not front matter) for a whole-word match. Replace + with `term`, preserving sentence-initial capitalisation and plural `s`. + This is a **fix**. +- If `case_sensitive: true`, also flag case variants of `term` (e.g. "openfn" + in prose). **Fix**. +- For entries with `product_noun: true`, do not flag ordinary-English use. + Only flag variants. + +Also flag "adapter" anywhere in prose regardless of glossary, since it is the +single most common error. **Fix**. + +Strip code before matching: + +```bash +# crude but reliable: remove fenced blocks and inline code, then grep +perl -0pe 's/```.*?```//gs; s/`[^`]*`//g' "$f" | grep -n -i -w -E 'adapter|open fn|workorder|data clip' +``` + +### 2. Heading hierarchy (`heading-hierarchy`, `heading-case`) + +- Pages must not contain an `# H1` heading in the body. The title comes from + front matter `title`. A body H1 is a **fix**: convert to `##` and shift its + children down one level, unless the front matter has no `title`, in which + case move the H1 text into `title:` and delete the heading. +- Heading levels must not skip (a `##` followed by `####`). **Fix** by + promoting the deeper heading. +- Heading case must be consistent within a page. The repo convention is + sentence case with product nouns capitalised ("Create or edit a Step"). + Flag a page that mixes Title Case and sentence case. Converting case is a + **suggestion**, not a fix, because headings are link anchors and changing + them can break inbound `#fragment` links. Exception: if you convert, grep + the repo for the old anchor first and update every reference in the same + change; then it is a **fix**. +- Duplicate heading text within one page produces duplicate anchors. **Fix** + by making the second unique, then update any in-page links to it. + +```bash +grep -n -E '^#{1,6} ' "$f" +``` + +### 3. Internal links (`internal-link`) + +Extract every `](...)` and `href="..."` target that starts with `/`, `./`, +`../`, or `#`. + +- Site-absolute links (`/documentation/...`, `/adaptors/...`, `/articles/...`) + must resolve to an existing page id or a redirect `from` in + `docusaurus.config.js`. Map `/documentation/<a>/<b>` to `docs/<a>/<b>.md` + or `.mdx`, honouring `slug:` and `id:` front matter overrides. +- Relative `.md` links must resolve on disk. +- `#fragment` links must match a heading in the target page after Docusaurus + slugification (lower case, spaces to `-`, punctuation removed). +- Links to `/adaptors/packages/...` cannot be checked without running + `yarn generate-adaptors`. Run it if network allows; otherwise mark + **uncertain** and list them in the PR. + +Broken internal links are a **fix** when the intended target is unambiguous +(one candidate page with matching title or slug). Otherwise a **question**. + +The fastest authoritative check is the build, because `onBrokenLinks` is +`throw`: + +```bash +yarn build 2>&1 | grep -A3 -i 'broken' +``` + +### 4. External links (`external-link`) + +For every `http://` or `https://` link outside code blocks: + +```bash +curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' -L --max-time 15 -A 'openfn-docs-lint' "$url" +``` + +- 404, 410, or connection failure on two tries five seconds apart: dead. + Replacing a dead link is a **fix** only when the page has an obvious + successor (a 301 target, or the same path on a renamed domain). Otherwise + a **suggestion**: propose removal or an archive.org link. +- 403 and 429 are not dead. Mark **uncertain** and skip. +- `http://` links that respond on `https://` are a **fix**: upgrade them. +- Do not check links inside code blocks (they are examples, not references). + +### 5. Orphaned pages (`orphan-page`) + +A page is orphaned when its id appears in no sidebar and no other page links +to it. + +```bash +# every doc id on disk +find docs -type f \( -name '*.md' -o -name '*.mdx' \) | sed -E 's|^docs/||; s|\.mdx?$||' | sort > /tmp/ids +# every id referenced in the sidebar +node -e 'console.log(JSON.stringify(require("./sidebars-main.js")))' | grep -o -E '"[a-z0-9/_.-]+"' | tr -d '"' | sort -u > /tmp/sidebar +comm -23 /tmp/ids /tmp/sidebar +``` + +Then, for each candidate, grep `docs/`, `articles/`, `adaptors/*.md`, and +`src/` for links to it. Honour `id:` and `slug:` overrides. A page that is +genuinely unreachable is a **suggestion** (add to sidebar, or delete), never a +fix: someone may be drafting it. Note in the PR whether the page looks +finished. + +Pages under `versioned_docs/` are checked against +`versioned_sidebars/version-legacy-sidebars.json`, and findings are +suggestions only. + +### 6. Front matter (`frontmatter`) + +Every page in `docs/` and `adaptors/*.md` must have YAML front matter with at +least `title`. **Fix** a missing `title` by using the body H1 (then remove the +H1) or, failing that, the file name in sentence case (record the invented +title as a **suggestion** so a human confirms it). + +Every page under `i18n/**` must have all five translation fields: +`translation_source_hash`, `translation_review_status`, `translation_model`, +and, when status is `human-reviewed`, `translation_reviewer` and +`translation_review_date`. A missing field on a translated page is a +**question** unless the translate skill is about to regenerate the page. + +Front matter must parse as YAML. Unquoted values containing `:` are the usual +cause of failure. **Fix** by quoting. + +### 7. Code block language tags (`code-language`) + +Every fenced code block must have a language after the opening fence. + +```bash +awk '/^```/{ if (open) { open=0 } else { open=1; if ($0 ~ /^```\s*$/) print FILENAME":"NR": untagged fence" } }' "$f" +``` + +Infer the language from content and add it. Use these tags: `js` for job +code and JavaScript, `json`, `yaml`, `bash` for shell commands, `text` for +console output, logs, and anything else. This is a **fix**. If you cannot +tell what the block is, tag it `text`. + +### 8. Image alt text (`image-alt`) + +Every `![...](...)` must have non-empty alt text that describes what the +image shows, not "image" or "screenshot". Every `<img>` must have a +non-empty `alt` attribute. + +- Empty alt: **fix**. Write alt text from the surrounding paragraph and the + file name (`anatomy_of_step.webp` next to "A Step includes these key + components" becomes "Diagram of a Step showing its name, adaptor, credential, + and job expression"). +- Alt text that is only "image", "screenshot", "img", or the file name: + **fix** the same way. +- Image path that does not exist under `static/` (for `/img/...` paths) or + relative to the page: **fix** if there is exactly one file with the same + base name under `static/img/`; otherwise **question**. + +### 9. Admonition spacing (`admonition-spacing`) + +`:::tip`, `:::note`, `:::info`, `:::warning`, `:::caution`, and `:::danger` +blocks need a blank line after the opening line and before the closing `:::`, +or MDX renders them wrong. **Fix**. + +## Applying fixes + +- Apply fixes with minimal edits. Do not reflow paragraphs by hand; run + Prettier afterwards and let it wrap at 80 columns: + + ```bash + npx prettier --write <changed files> + ``` + +- Do not touch a line for any reason other than the finding. +- After all fixes, run `yarn build` (or `yarn start-offline` when offline) and + confirm zero broken-link errors and zero MDX compile errors. + +## Output + +A list of findings in the shared format: + +``` +[fix|suggestion|question] <file>:<line> — <rule> — <what is wrong> — <what was done or proposed> +``` + +Group by rule, then by file. Fixes were applied and go under "What changed" in +the PR. Suggestions go under "Suggestions (not applied)" with the proposed +text. Questions go under "Questions". Uncertain external links go under +"Questions" as a single bullet listing the URLs. + +Report counts at the top: + +``` +Files checked: N. Fixes applied: N. Suggestions: N. Questions: N. Suppressed by style-exceptions.yml: N. +``` + +## When a human rejects one of your fixes + +That is a signal for the corrections-capture skill +(`.agents/skills/corrections-capture.md`). Do not argue in the PR. Record the +exception so it is not raised again. diff --git a/.agents/skills/screenshot-triage.md b/.agents/skills/screenshot-triage.md new file mode 100644 index 000000000000..b15a70a85d67 --- /dev/null +++ b/.agents/skills/screenshot-triage.md @@ -0,0 +1,200 @@ +# Skill: Screenshot triage + +Find screenshots that are probably out of date and rank them for a human to +retake. This skill never retakes, edits, or deletes an image. + +## Inputs + +- A section (sidebar category or `docs/` directory), or the whole + `static/img/` tree if the user asks for a full triage. +- Read access to `OpenFn/lightning` (the web app UI) cloned to a scratch + directory. For CLI screenshots, `OpenFn/kit`. + +```bash +SCRATCH=${SCRATCH:-/tmp/openfn-src} +git clone --filter=blob:none https://github.com/OpenFn/lightning "$SCRATCH/lightning" +git clone --filter=blob:none https://github.com/OpenFn/kit "$SCRATCH/kit" +``` + +Full history is needed for dates, so do not use `--depth`. `--filter=blob:none` +keeps it fast. + +## Where images live + +All images are in `static/img/` and referenced from pages as `/img/<file>`. +Formats in use: `.webp`, `.png`, `.gif`, `.svg`, `.jpg`. Treat `.svg` files as +diagrams or logos, not screenshots, unless the alt text says otherwise. + +## Process + +### 1. List the images in scope + +For a section, collect every image referenced by the section's pages: + +```bash +grep -h -o -E '\]\(/img/[^)]+\)|src="/img/[^"]+"' docs/<section>/*.md \ + | grep -o -E '/img/[^)"]+' | sort -u +``` + +For a full triage, list `static/img/` and also compute which images are +referenced nowhere (candidates for deletion; report, do not delete). + +### 2. Date each image + +Last commit that touched the file in this repo: + +```bash +git log -n 1 --format='%H %cs' -- static/img/<file> +``` + +Also note the referencing page, the line, the alt text, and the two lines +of prose before the image. If the image was optimised in bulk by +`scripts/optimize-images.js` (look for a commit touching many images at +once), use the commit before that bulk commit as the real date; a +re-encode is not a retake. + +### 3. Identify the UI the image depicts + +Classify each image by combining file name, alt text, surrounding prose, and +the page's section. Map it to a **UI area** and then to **source paths** in +the product repo. Use this table; extend it when you meet an unlisted area. + +| UI area | Signals in file name / alt / prose | Lightning source paths (relative to repo root) | +| -------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| Workflow canvas | canvas, workflow diagram, nodes, edges, add step, plus icon | `assets/js/workflow-diagram/`, `lib/lightning_web/live/workflow_live/` | +| Step / job editor (Inspector) | inspector, editor, job code, adaptor picker, credential picker | `lib/lightning_web/live/workflow_live/`, `assets/js/collaborative-editor/`, `assets/js/monaco/`, `assets/js/picker/`, `assets/js/adaptor-docs/`, `assets/js/manual-run-panel/` | +| Triggers | trigger, webhook URL, cron, kafka | `lib/lightning_web/live/workflow_live/`, `lib/lightning/workflows/trigger.ex`, `lib/lightning/workflows/triggers/` | +| Runs / history / inspect run | run, history, work order, log, dataclip, output, rerun | `lib/lightning_web/live/run_live/`, `lib/lightning_web/live/dataclip_live/`, `assets/js/log-viewer/` | +| Credentials | credential, OAuth, connect, authorise | `lib/lightning_web/live/credential_live/`, `lib/lightning/credentials/` | +| Project settings | settings, collaborators, retention, GitHub sync, webhook auth, VCS | `lib/lightning_web/live/project_live/`, `lib/lightning_web/components/github_components.ex` | +| Sandboxes | sandbox, clone project, merge | `lib/lightning_web/live/sandbox_live/`, `lib/lightning_web/components/sandbox_settings_banner.ex` | +| Channels | channel, channel request | `lib/lightning_web/live/channel_live/`, `lib/lightning_web/live/channel_request_live/`, `lib/lightning/channels/` | +| Audit | audit, audit log, audit trail | `lib/lightning_web/live/audit_live/`, `lib/lightning/auditing/` | +| Dashboard / project list | dashboard, projects, metrics, overview | `lib/lightning_web/live/dashboard_live/`, `lib/lightning_web/live/project_live/` | +| User profile / tokens | profile, API token, MFA, password | `lib/lightning_web/live/profile_live/`, `lib/lightning_web/live/tokens_live/` | +| AI Assistant | assistant, chat, AI | `lib/lightning_web/live/ai_assistant/`, `lib/lightning/ai_assistant/` | +| Collections | collection, key/value | `lib/lightning_web/live/collection_live/`, `lib/lightning/collections/` | +| Login / signup / layout / navbar | login, register, sidebar, menu, navbar | `lib/lightning_web/components/layouts/`, `lib/lightning_web/live/user_live/`, `assets/css/` | +| Global styling | (applies to every screenshot) | `assets/css/app.css`, `assets/tailwind.config.ts`, `lib/lightning_web/components/core_components.ex`, `lib/lightning_web/components/layout_components.ex` | +| CLI terminal output | terminal, console, `openfn` prompt | `kit/packages/logger/src/`, `kit/packages/cli/src/<command>/` | +| Third-party UI (Kobo, DHIS2, GSheets, CommCare) | the other product's name | none in OpenFn repos; mark `external` | + +Paths change. If a listed path does not exist in the checkout, run +`git log --diff-filter=R --summary -- <path>` to follow the rename, or grep +for the LiveView module name. + +Confidence: record `high` when file name and alt text agree with the prose, +`medium` when only one of them does, `low` when you inferred from the +section alone. + +### 4. Date the UI code + +For each mapped source path set, find the newest commit that touched any of +them, excluding pure test and formatting commits: + +```bash +cd "$SCRATCH/lightning" +git log -n 1 --format='%H %cs %s' -- <path1> <path2> ... +``` + +Also record the newest commit touching the **global styling** paths, because +a theme or component-library change re-dates every screenshot. Use the later +of the two dates for each image. + +Then list the commit subjects between the image date and today for the +mapped paths, to say what likely changed: + +```bash +git log --since=<image-date> --format='%cs %s' -- <paths> | head -20 +``` + +Keep the subjects that sound user-visible (rename, redesign, move, add +button, new page, layout, colour, icon). Drop refactors, test changes, and +dependency bumps. + +### 5. Flag suspects + +An image is a **suspect** when the UI code date is later than the image +date. Compute the gap in days. + +Not suspects, but report them in a separate list: + +- `external` images (third-party UIs). Say which product and the image + date; a human decides. +- Diagrams and logos (`.svg`, or alt text says "diagram"). +- Images referenced by no page (orphans). + +### 6. Rank + +Sort suspects by: + +1. Gap size (UI code date minus image date), largest first. +2. Then by number of user-visible commits in the gap, most first. +3. Then by the page's position in the docs: anything under "Get Started" or + "Tutorials" ranks above the same gap elsewhere. + +Do not rank `low` confidence mappings above `high` ones with a similar gap; +if a low-confidence mapping would land in the top five, say so. + +## Output + +``` +Scope: <section or full>. Images: N. Suspects: N. External: N. Diagrams/logos: N. Orphans: N. +Lightning checked at <sha> (<date>). Kit checked at <sha>. + +Suspect screenshots (ranked): + +| # | Image | Page:line | Image date | UI area (confidence) | Last UI change | Gap (days) | What likely changed | +|---|-------|-----------|------------|----------------------|----------------|------------|---------------------| +| 1 | static/img/4.1_new_job.webp | docs/tutorials/kobo-to-dhis2.md:88 | 2023-02-14 | Step editor (high) | 2026-08-30 | 1293 | Inspector redesigned; adaptor picker moved to header; "Save & Run" renamed | + +External (human decides): +- static/img/2.3_kobo_rest.webp — KoboToolbox REST settings — 2023-02-14 + +Diagrams and logos (skipped): ... + +Orphaned images (referenced by no page): ... +``` + +Put the ranked table in the PR under "Suspect screenshots (ranked)". Cap at +the top fifteen and put the rest in a collapsed `<details>` block. + +Actions you may take in the docs repo: + +- **fix**: an image whose alt text is wrong about what the image shows (per + the mapping you just did) gets corrected alt text. +- Everything else is a report. Do not delete orphans, do not replace images, + do not edit the images. + +## Extension point: automated capture (not implemented) + +This skill is designed so that capture can plug in later without changing +the triage above. Do not implement any of this now. + +`OpenFn/lightning` already has Playwright end-to-end tests: config at +`assets/playwright.config.ts`, specs under `assets/test/e2e/specs/` (a +`smoke/` suite and a `collaborative/` suite at the time of writing). They +cover a subset of the UI areas above and do not yet emit docs screenshots. +When they do, or when a docs-specific capture suite is added, add a step +**7. Capture** after ranking: + +- Input: the ranked suspect list from step 6, each row carrying its + `UI area` and source paths. +- A mapping file in this repo, `screenshot-capture-map.yml` (does not exist + yet), keyed by image path, that names the Playwright test file and test + title that reaches the right screen, the selector or viewport to capture, + and any fixture data needed. Rows without a mapping stay report-only. +- The capture command runs the named test from `assets/` in the Lightning + checkout with a capture flag (for example + `npx playwright test test/e2e/specs/<file> -g "<title>"` with a custom + reporter or a `DOCS_SCREENSHOTS=1` env var the spec checks) against a + seeded local Lightning instance, writes the new image to + `static/img/<same file name>` in this repo, and records the Lightning + commit it was captured at. +- Use the test's own selectors for the capture region so the image tracks + the UI. Do not hard-code pixel crops. +- Replacement images go into the PR alongside the ranked table, with a + before/after pair in the description, and stay a **suggestion** until a + human approves the PR. + +Until that mapping file and those tests exist, this skill ends at step 6. diff --git a/.agents/skills/translate.md b/.agents/skills/translate.md new file mode 100644 index 000000000000..d7e79764c794 --- /dev/null +++ b/.agents/skills/translate.md @@ -0,0 +1,262 @@ +# Skill: Translate + +Translate English docs pages into the target locales while respecting the +governance rules: glossary terms stay in English, human-reviewed pages are +never overwritten, and fenced blocks are never retranslated. + +English is the canonical source. Translations are generated artefacts that +live in the same repo and branch. + +## Target locales + +`es` (Spanish) and `fr` (French). Add a locale only when the user asks and +`docusaurus.config.js` lists it under `i18n.locales`. + +## Paths + +| English source | Translation | +| ------------------------ | ------------------------------------------------------------------ | +| `docs/<a>/<b>.md` | `i18n/<locale>/docusaurus-plugin-content-docs/current/<a>/<b>.md` | +| `adaptors/<name>.md` | `i18n/<locale>/docusaurus-plugin-content-docs-adaptors/current/<name>.md` (hand-written overviews only) | +| `adaptors/packages/**` | never translated | +| `adaptors/library/**` | never translated | +| `articles/**` | not in scope unless the user asks | +| `versioned_docs/**` | never translated | + +Keep the same file name and extension as the source. + +## Preconditions + +Run these before translating anything. Stop with a **question** if any fails. + +1. The English section has no open **fix** findings from lint, accuracy, or + fresh-user evaluation. Translating a page you are about to change wastes + the run. +2. `docusaurus.config.js` has an `i18n` block whose `locales` includes the + target locale. If it does not, do not add it yourself: enabling a locale + changes what `yarn build` produces and deploys. Record the question + "Enable `<locale>` in `docusaurus.config.js` i18n config?" and stop. +3. `/i18n` is not listed in `.gitignore`. If it is, stop and ask; committed + translations are the design, and the ignore rule contradicts it. +4. `glossary.yml` and `translation-rules.yml` parse as YAML. + +## Front matter fields + +Every translated page carries the source page's own front matter (translated +`title` and `sidebar_label`; untouched `id`, `slug`, `keywords`) plus: + +```yaml +translation_source_hash: <full git commit SHA of the last commit that touched the English page> +translation_review_status: machine # machine | human-reviewed | needs-review +translation_reviewer: # GitHub handle, only when human-reviewed +translation_review_date: # YYYY-MM-DD, only when human-reviewed +translation_model: <model identifier you are running as, e.g. the configured model id> +``` + +Get the source hash with: + +```bash +git log -n 1 --format=%H -- docs/<path>.md +``` + +Use the commit that last touched the file, not `HEAD`, so a repo-wide commit +does not invalidate every translation. + +Set `translation_model` to the exact model identifier of the session (not a +marketing name). If you cannot determine it, write `unknown` and flag it. + +## Inline override fences + +Humans mark translated content that must survive regeneration: + +```markdown +<!-- do-not-retranslate --> +Este párrafo fue corregido por un revisor humano. +<!-- /do-not-retranslate --> +``` + +Rules: + +- Everything between the opening and closing marker, including the markers, + is copied byte for byte into the regenerated page at the same position + relative to the surrounding structure (same heading, same paragraph + index). +- If the English content that the fenced block corresponds to was deleted, + keep the fenced block and add a **question** to the PR: "Fenced block at + L<line> has no English counterpart any more; delete or keep?" +- If a fence is unclosed, treat everything to end of file as fenced and add + a **fix** to the PR closing the fence; do not regenerate the page until a + human confirms. +- Nested fences are invalid. Stop and ask. + +## Process, per page and per locale + +### 1. Load rules + +Read `glossary.yml`. Build the set of terms with `translate: false` and the +`patterns` list. Read `translation-rules.yml` and keep the rules whose +`locale` is the target or `*`. + +### 2. Decide the action + +```bash +SRC=docs/<path>.md +DST=i18n/<locale>/docusaurus-plugin-content-docs/current/<path>.md +SRC_HASH=$(git log -n 1 --format=%H -- "$SRC") +``` + +| Translation exists? | `translation_review_status` | `translation_source_hash` == `$SRC_HASH`? | Action | +| ------------------- | --------------------------- | ----------------------------------------- | ------------------------------- | +| no | | | **A. Full translation** | +| yes | `machine` or `needs-review` | any | **B. Regenerate with fences** | +| yes | `human-reviewed` | yes | **Skip.** Current and approved. | +| yes | `human-reviewed` | no | **C. Suggested diff PR** | +| yes | missing or invalid | | Treat as `needs-review`; add a **question** noting the missing field | + +### 3A. Full translation + +1. Split the source into segments: front matter, headings, paragraphs, + lists, tables, admonitions, code blocks, HTML/JSX blocks, images, links. +2. Translate prose segments. Rules: + - Glossary terms and pattern matches stay verbatim, including their + capitalisation. When a glossary entry has `product_noun: true`, keep + the word untranslated only where it names the OpenFn concept; translate + ordinary-English uses ("run the command" may be translated; "a Run" may + not). + - Apply every matching rule in `translation-rules.yml`. + - Default register: Spanish "tú", French "vous", unless a rule says + otherwise. + - Translate `title`, `sidebar_label`, admonition titles (`:::tip Título`), + table headers, image alt text, and link text. + - Do not translate `id`, `slug`, `keywords`, heading anchors set with + `{#anchor}`, HTML attribute names, or anything inside backticks. + - Preserve Markdown and MDX structure exactly: same heading levels, same + list markers, same admonition types, same `<details>`/`<Tabs>` + components with the same props. +3. Copy code blocks (fenced and inline) byte for byte. Translate only + comments inside fenced blocks when the block's language is `js`, `bash`, + `yaml`, or `json` with `//` or `#` comments and the comment is prose, not + a command. Leave string literals, keys, and identifiers alone. +4. Rewrite internal links: + - `/documentation/...`, `/adaptors/...` and `/articles/...` become + `/<locale>/documentation/...` etc. Docusaurus resolves them at build + time, but explicit locale prefixes keep translated pages linking to + translated pages. Exception: links into `adaptors/packages/...` stay + unprefixed, because those pages are English-only. + - Relative `.md` links stay relative (they resolve inside the locale + tree). + - `#fragment` anchors: Docusaurus slugifies the translated heading, so a + translated heading changes the anchor. Either add an explicit + `{#original-anchor}` to the translated heading (preferred; keeps + English anchors stable across locales) or update the fragment. Do the + former. +5. Write front matter: source fields plus the five translation fields with + `translation_review_status: machine`. +6. Write the file at `$DST`, creating directories as needed. + +### 3B. Regenerate with fences + +1. Read the existing translation. Extract every + `<!-- do-not-retranslate -->` block with its position (the nearest + preceding heading and the paragraph index under it). +2. Perform 3A on the current English source. +3. Re-insert each fenced block at the matching position. If the position no + longer exists, append it under the nearest surviving heading and record a + **question**. +4. Keep `translation_review_status` as it was if it was `needs-review`; + otherwise set `machine`. Update `translation_source_hash` and + `translation_model`. + +### 3C. Suggested diff for a human-reviewed page + +Never write to `$DST`. + +1. Compute the English change since the recorded hash: + + ```bash + OLD=$(grep -m1 translation_source_hash "$DST" | awk '{print $2}') + git diff "$OLD" "$SRC_HASH" -- "$SRC" + ``` + +2. Translate only the changed or added English hunks, following 3A rules. +3. Produce a unified diff against the current `$DST` that applies those + translated hunks at the corresponding positions and updates + `translation_source_hash` to `$SRC_HASH`. Leave + `translation_review_status: human-reviewed`, `translation_reviewer`, and + `translation_review_date` untouched in the diff; the reviewer decides + whether to keep the status. +4. Put the diff in a **separate PR** titled + `translate(<locale>): suggested update for <path> (human-reviewed)`, + request review from `translation_reviewer`, and reference the English + commit range in the body. If several human-reviewed pages in the same + section need updates, one PR for all of them is fine. Do not mix these + diffs into the main section PR. + +## Quality checks + +Run on every page you wrote (3A and 3B) before committing. Any failure is a +**fix** you make now. + +1. **Glossary**: every `translate: false` term that appears in the English + prose appears the same number of times, verbatim, in the translated + prose. Product nouns with `product_noun: true` may appear fewer times + only if the English used the word in its ordinary sense. + + ```bash + for t in OpenFn Lightning adaptor workflow; do + printf '%s: %s -> %s\n' "$t" "$(grep -o -i -w "$t" "$SRC" | wc -l)" "$(grep -o -i -w "$t" "$DST" | wc -l)" + done + ``` + +2. **Code blocks**: extract all fenced blocks from source and translation; + after stripping comment lines they must be identical, in the same order. + + ```bash + diff <(awk '/^```/{f=!f; print; next} f && !/^\s*(\/\/|#)/' "$SRC") \ + <(awk '/^```/{f=!f; print; next} f && !/^\s*(\/\/|#)/' "$DST") + ``` + +3. **Structure**: same count of headings per level, same count of fenced + blocks, admonitions, images, and tables. + +4. **Links**: every internal link in the translation resolves. Run the + locale build: + + ```bash + yarn docusaurus build --locale <locale> + ``` + + `onBrokenLinks: 'throw'` makes this authoritative. + +5. **Front matter**: all required translation fields present, hash is 40 hex + characters, status is one of the three allowed values, model is set. + +6. **Fences**: every `<!-- do-not-retranslate -->` in the old file is + present in the new one, byte for byte. + +## Output + +``` +Locale: es +Pages: N. Full: N. Regenerated: N. Skipped (human-reviewed, current): N. Suggested-diff PRs: N. + +| Source | Action | Source hash | Fences kept | Checks | +| ------ | ------ | ----------- | ----------- | ------ | +| docs/jobs/state.md | regenerate | a1b2c3d | 2 | pass | + +Questions: +[question] i18n/es/.../state.md L40 — fenced block has no English counterpart since <hash> — keep or delete? +``` + +Commit translations per locale: `translate(es): <section>`. Count each +translated file toward the 20-file limit in `AGENTS.md`. + +## Do not + +- Do not translate generated adaptor reference pages, ever. +- Do not translate `versioned_docs/`. +- Do not "improve" the English source while translating. Record the issue as + a finding for the next English pass. +- Do not change `translation_review_status` to `human-reviewed`. Only a + human sets that, by hand, with their handle and the date. +- Do not commit a translation whose quality checks fail. diff --git a/.gitignore b/.gitignore index a35029f45316..504d16736448 100644 --- a/.gitignore +++ b/.gitignore @@ -13,8 +13,7 @@ .docusaurus .cache-loader -# translation -/i18n +# translation: i18n/ is committed (translations are generated artefacts kept in-repo, see AGENTS.md) # Misc .DS_Store diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000000..d7354132eca4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,230 @@ +# AGENTS.md: docs maintenance agent for OpenFn/docs + +You are the documentation maintenance agent for the OpenFn docs site +(https://docs.openfn.org). This repo is a Docusaurus 3 project. Your job is to +keep one section of the docs at a time accurate, readable, complete, lint-clean, +and (once the English is clean) translated. + +Read this file first. Then load only the skill files you need from +`.agents/skills/`. Each skill file is self-contained. + +## Repo map + +| Path | What it is | Editable? | +| ------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `docs/**/*.md`, `docs/**/*.mdx` | Hand-written English docs (canonical source) | Yes | +| `sidebars-main.js` | Navigation for `docs/` | Yes | +| `adaptors/*.md`, `adaptors/intro.mdx` | Hand-written adaptor overview pages | Yes | +| `adaptors/packages/**` | Generated adaptor reference (functions, config schema, changelog, readme) from JSDoc | **No.** Fix at source in `OpenFn/adaptors` | +| `adaptors/library/jobs/auto/**` | Generated job library | **No.** Generated at build | +| `sidebars-adaptors.js` | Adaptor navigation (mostly derived from generated `publicPaths.json`) | Only the hand-written parts | +| `articles/` | Blog-style help articles | Yes, only when a section named includes it | +| `versioned_docs/version-legacy/**` | Frozen v1 docs (banner: unmaintained) | No. Record findings as suggestions only | +| `static/img/**` | Images and screenshots | Yes (metadata only; never retake images) | +| `i18n/{locale}/docusaurus-plugin-content-docs/current/` | Translations (generated artefacts, same repo, same branch) | Yes, via the translate skill rules | +| `glossary.yml` | Product terms that are never translated, plus spelling variants lint should flag | Yes, via corrections-capture | +| `style-exceptions.yml` | Lint findings humans have rejected; do not flag again | Yes, via corrections-capture | +| `translation-rules.yml` | Locale-specific phrasing rules learned from human edits | Yes, via corrections-capture | +| `docusaurus.config.js` | Site config | Ask before editing | + +Product source repos (read-only, for verification): + +- `OpenFn/lightning`: the web app (Platform). Elixir/Phoenix. UI lives under + `lib/lightning_web/` and `assets/`. +- `OpenFn/kit`: the CLI (`@openfn/cli`), runtime, compiler, and deploy + tooling. Note that `@openfn/language-common` lives in `OpenFn/adaptors`, + not here. +- `OpenFn/adaptors`: the adaptor monorepo. JSDoc in `packages/<name>/src/` is + the source of every page under `adaptors/packages/`. + +The generated adaptor pages are not in git. `yarn generate-adaptors` fetches +`docs.json` from the `docs` branch of `OpenFn/adaptors` and writes the pages at +build time. Any inaccuracy you find there is a JSDoc bug in +`OpenFn/adaptors`, not a docs-repo bug. + +## Skills + +| Skill | File | When | +| ---------------------- | ------------------------------------------- | ------------------------------------------------------------- | +| Lint | `.agents/skills/lint.md` | Always first. Deterministic style and structure checks. | +| Accuracy check | `.agents/skills/accuracy-check.md` | After lint. Verify every claim against product code. | +| Fresh-user evaluation | `.agents/skills/fresh-user-eval.md` | After accuracy. Read cold, try to do the task. | +| Gap analysis | `.agents/skills/gap-analysis.md` | After fresh-user eval. What is missing from this section? | +| Translate | `.agents/skills/translate.md` | Last, and only once the English section has no open fixes. | +| Corrections capture | `.agents/skills/corrections-capture.md` | Whenever a human has overridden a previous agent output. | +| Screenshot triage | `.agents/skills/screenshot-triage.md` | On request, or when a section contains images. | + +## Default execution order + +1. Confirm the section (see "Scope" below). +2. Run **lint**. Apply fixes. Record suggestions. +3. Run **accuracy check** on every page in the section. Apply fixes. Record + suggestions and questions. Draft `OpenFn/adaptors` issues for generated-page + problems. +4. Run **fresh-user evaluation** on every page in the section. Apply fixes. + Record scores, suggestions, and questions. +5. Run **gap analysis** for the section. Record the ranked gap list. Do not + write new pages unless the user asked for them. +6. If the section contains images, run **screenshot triage** and record the + ranked list. Never retake screenshots. +7. Only if steps 2 to 4 left zero open fixes and zero unanswered questions for + the section: run **translate** for each target locale (`es`, `fr`). +8. Open the PR (see "Stopping and the PR"). + +If the user names a single skill, run only that skill on the named section and +still finish with a PR. + +## Scope: one section at a time + +A "section" is one top-level or nested category in `sidebars-main.js` (for +example "Get Started", "Write Jobs", "Platform > Monitor History", "CLI"), or +one directory under `docs/`, or a single page if the user names one. + +- Never process the whole site in one run. +- If the user has not named a section, stop and ask which one. List the + categories from `sidebars-main.js` so they can pick. +- Stay inside the section. If a finding requires a change outside it (a + broken link target, a glossary term), make that single change and note it + under "Also touched" in the PR. + +## Classifying findings + +Every finding from every skill gets exactly one class: + +- **fix**: Objectively wrong or mechanically checkable, and the correct value + is known from the code, the build, or a config file. Apply it directly. + Examples: typo in a CLI flag, a dead link, a heading that skips a level, a + code block missing a language tag, an incorrect default value verified in + source. +- **suggestion**: A judgement call about wording, structure, emphasis, or + scope where a reasonable author could disagree. Do not apply. Record it in + the PR description with the proposed text so a human can accept it. +- **question**: The docs and the code disagree and you cannot tell which is + intended, or the page implies a product behaviour you cannot verify, or the + right fix depends on a decision you do not own. Do not guess. Record it in + the PR description as a question with what you checked and what the + candidates are. If the question blocks the rest of the section, stop and + ask the user. + +When in doubt between fix and suggestion, choose suggestion. When in doubt +between suggestion and question, choose question. + +## Hard rules + +1. **Never edit a page whose front matter has + `translation_review_status: human-reviewed`.** Produce the change as a + suggested diff in the PR description (or a separate PR if the diff is + large) for the named `translation_reviewer` to approve. +2. **Never edit generated adaptor reference pages** (`adaptors/packages/**`, + `adaptors/library/jobs/auto/**`). Write an issue for `OpenFn/adaptors` + naming the package, the JSDoc block, and the correction. Put the draft + issue body in the PR description under "Upstream issues". Only file the + issue if the user has asked you to file issues. +3. **Never retranslate content inside `<!-- do-not-retranslate -->` fences.** +4. **Never translate glossary terms.** Load `glossary.yml` before touching any + translation. +5. **Never edit `versioned_docs/`.** Legacy v1 docs are frozen. +6. **Never change `docusaurus.config.js`, `package.json`, or CI workflows** + without asking first. These affect the production build. +7. **Never retake, crop, or regenerate screenshots.** Triage only. +8. **Never commit secrets, personal data, or internal URLs** you find in + product repos. +9. **Never use skipped-test, disabled-check, or "ignore" workarounds** to get + the build green. If `yarn build` fails after your changes, fix the cause. + +## Stopping and the PR + +Stop and open a PR when either happens first: + +- The section is done (every skill in the order above has run or been + explicitly skipped), or +- You have changed **20 files**. Count every created, modified, or deleted + file, including translations and YAML config files. When you reach 20, stop + the current skill cleanly, do not start another, and open the PR. Say in the + PR which pages in the section were not reached. + +Before opening the PR: + +1. Run `yarn build` (or `yarn start-offline` if network is unavailable, then + confirm the changed pages render). `onBrokenLinks` is set to `throw`, so a + broken internal link fails the build. +2. Run Prettier on changed markdown: `npx prettier --write <files>`. The repo + uses `.prettierrc` with `proseWrap: always` and `printWidth: 80`. +3. Re-read your diff. Remove anything that is not a **fix**. + +Work on a branch named `docs-agent/<section-slug>` unless the user gave you a +branch. Commit in small, labelled commits (`lint: ...`, `accuracy: ...`, +`fresh-user: ...`, `translate(es): ...`). + +The PR description follows `.github/pull_request_template.md`. Tick "I have used +Claude Code" under AI Usage. Then add these sections: + +```markdown +## Section + +<sidebar category or directory>, <N> pages. Skills run: <list>. + +## What changed + +- <page>: <one line per fix, grouped by skill> + +## Suggestions (not applied) + +- <page> L<line>: <current text> → <proposed text>. Reason: <one sentence>. + +## Questions + +- <page>: <what the docs say> vs <what the code says at repo/path:line>. Which is intended? + +## Skipped + +- <page>: human-reviewed translation, suggested diff below +- <page>: generated, see Upstream issues +- <pages not reached because the 20-file limit was hit> + +## Upstream issues (OpenFn/adaptors) + +<draft issue bodies, one per package> + +## Scores (fresh-user evaluation) + +| Page | Readability | Completeness | +| ---- | ----------- | ------------ | + +## Gaps (ranked) + +<ranked list from gap analysis> + +## Suspect screenshots (ranked) + +<ranked list from screenshot triage> +``` + +Omit any section that is empty. + +## Shared finding format + +Every skill records findings in this shape so they can be merged into the PR: + +``` +[fix|suggestion|question] <file path>:<line> — <what is wrong> — <what to do> +``` + +Line numbers refer to the file as it was before your edits. + +## Conventions you must respect while editing + +- Front matter is YAML between `---` fences. Pages in `docs/` use `title`, + optionally `sidebar_label`, `id`, `slug`, `keywords`. Do not invent new + fields except the translation fields defined in + `.agents/skills/translate.md`. +- Internal links use absolute site paths: `/documentation/<path>` for `docs/`, + `/adaptors/<path>` for adaptors, `/articles/<path>` for articles. +- Docusaurus admonitions (`:::tip`, `:::note`, `:::warning`, `:::info`, + `:::caution`) must have a blank line before and after the fences. +- Images live in `static/img/` and are referenced as `/img/<file>`. Every image + needs alt text. +- The spelling is **adaptor**, never "adapter". Terminology is defined in + `docs/get-started/terminology.md` and pinned in `glossary.yml`. +- Do not rewrite a page's voice or structure under the banner of a fix. Fixes + are local. diff --git a/glossary.yml b/glossary.yml new file mode 100644 index 000000000000..da5ec6dbb98f --- /dev/null +++ b/glossary.yml @@ -0,0 +1,194 @@ +# glossary.yml +# +# Purpose +# ------- +# Product vocabulary for the OpenFn docs. Two consumers read this file: +# +# 1. The translate skill (.agents/skills/translate.md). Any term with +# `translate: false` must appear verbatim in every translated page. +# 2. The lint skill (.agents/skills/lint.md). Any spelling in `variants` +# is flagged in English pages and replaced with `term`. +# +# The corrections-capture skill appends new entries when a human edit implies +# a terminology rule. Humans can edit this file directly too. +# +# Schema +# ------ +# terms: # list of glossary entries +# - term: string # canonical English spelling (required) +# translate: boolean # false = keep verbatim in all locales (default false) +# product_noun: boolean # true = the rule only applies when the word is used +# # as the OpenFn concept, not as ordinary English +# # (e.g. "run" the noun, not "run the command"). +# # Translators keep the product noun and may +# # translate the ordinary-English use. Default false. +# case_sensitive: boolean # true = lint flags case variants too (default false) +# variants: [string] # spellings lint should flag and replace with `term` +# note: string # guidance for humans and the agent +# locales: # optional. Only used when translate: true, to pin a +# es: string # specific rendering per locale instead of free +# fr: string # translation. +# +# patterns: # regexes that are never translated, for families +# - pattern: string # of identifiers too numerous to list (adaptor +# note: string # package names, CLI flags, env vars, ...) +# +# Matching is whole-word for `term` and `variants`. Code blocks, inline code, +# URLs, and front matter are always exempt from lint and translation. + +terms: + - term: OpenFn + translate: false + case_sensitive: true + variants: + - Open Fn + - Openfn + - openFn + note: The product and organisation name. Never localised. + + - term: Lightning + translate: false + case_sensitive: true + note: >- + The OpenFn web app (OpenFn/lightning). In user-facing docs prefer + "the OpenFn platform" or "the web app"; keep "Lightning" when the docs + refer to the repo or to self-hosting. + + - term: adaptor + translate: false + variants: + - adapter + - Adapter + note: >- + Always "adaptor", never "adapter". Also covers "adaptors", "Adaptor", + "Adaptors". Adaptor package names are matched by the pattern below. + + - term: workflow + translate: false + product_noun: true + note: >- + A Trigger plus Steps plus Paths configured on the Canvas or in + project.yaml. Keep "workflow" in translations when it names the OpenFn + object. + + - term: step + translate: false + product_noun: true + note: A unit of work inside a workflow. Was "job" in OpenFn v1. + + - term: job + translate: false + product_noun: true + note: >- + In v2 the job is the JavaScript expression a Step runs. Do not + "correct" v1 usage inside pages that are explicitly about v1 or + migration. + + - term: credential + translate: false + product_noun: true + note: Stored authentication configuration attached to a Step. + + - term: trigger + translate: false + product_noun: true + note: What starts a workflow. Types are webhook, cron, and kafka. + + - term: cron + translate: false + note: Trigger type and the scheduling syntax. + + - term: webhook + translate: false + note: Trigger type. One word, lower case, no hyphen. + + - term: run + translate: false + product_noun: true + note: >- + One execution of a workflow for a work order. Only the noun is + protected. "Run the CLI" is ordinary English and may be translated. + + - term: attempt + translate: false + product_noun: true + note: >- + Legacy name for a run. Do not replace it in migration or historical + pages. In pages about current v2 behaviour, prefer "run" and record the + change as a suggestion, not a fix. + + - term: work order + translate: false + variants: + - workorder + - work-order + note: The record created when a trigger fires; owns one or more runs. + + - term: project space + translate: false + note: Billing and hosting unit on the hosted OpenFn app. + + - term: project + translate: false + product_noun: true + note: Administrative grouping of workflows, credentials, and collaborators. + + - term: dataclip + translate: false + variants: + - data clip + - data-clip + note: A stored input or output state object. + + - term: collection + translate: false + product_noun: true + note: The Collections key-value store feature. Ordinary English use may be translated. + + - term: sandbox + translate: false + product_noun: true + note: A Lightning sandbox environment. + + - term: Canvas + translate: false + case_sensitive: true + note: The visual workflow editor in the web app. + + - term: Inspector + translate: false + case_sensitive: true + note: The step editing panel in the web app. + + - term: CLI + translate: false + case_sensitive: true + note: "@openfn/cli. Also keep every CLI command and flag verbatim." + + - term: state + translate: false + product_noun: true + note: >- + The `state` object passed between operations. Protected only when it + names the object (usually rendered in code as `state`). + + - term: operation + translate: false + product_noun: true + note: A function exported by an adaptor, e.g. `get()`, `upsert()`. + +patterns: + - pattern: "@openfn/[a-z0-9-]+" + note: npm package names (adaptors, CLI, runtime). + + - pattern: "language-[a-z0-9-]+" + note: Bare adaptor package names as they appear in the adaptor picker. + + - pattern: "\\bopenfn [a-z][a-z-]*" + note: CLI subcommands such as `openfn deploy`, `openfn pull`. + + - pattern: "\\b[A-Z][A-Z0-9_]{2,}\\b" + note: Environment variables and constants (OPENFN_API_KEY, WORKER_SECRET). + + - pattern: "\\bproject\\.yaml\\b" + note: The project state file name. diff --git a/style-exceptions.yml b/style-exceptions.yml new file mode 100644 index 000000000000..5669dc06b6fd --- /dev/null +++ b/style-exceptions.yml @@ -0,0 +1,44 @@ +# style-exceptions.yml +# +# Purpose +# ------- +# Lint findings that a human has rejected. The lint skill +# (.agents/skills/lint.md) loads this file and suppresses any finding that +# matches an entry, so the same rejected suggestion is not raised again. +# +# Entries are added by the corrections-capture skill +# (.agents/skills/corrections-capture.md) when a reviewer rejects or reverts a +# lint change in a PR, or by humans directly. +# +# Schema +# ------ +# exceptions: +# - rule: string # lint rule id, one of: +# # terminology, heading-hierarchy, heading-case, +# # internal-link, external-link, orphan-page, +# # frontmatter, code-language, image-alt, +# # admonition-spacing +# scope: string # glob of files the exception applies to. +# # "**" = whole repo, "docs/jobs/**" = a section, +# # "docs/jobs/state.md" = one page. +# match: string # optional. Substring or regex the finding text must +# # contain for the exception to apply (e.g. a heading +# # text, a link URL, a term). Omit to suppress the whole +# # rule within the scope. +# reason: string # why the human rejected it. Required. +# added_by: string # GitHub handle +# added_on: string # ISO date (YYYY-MM-DD) +# source_pr: string # PR URL or number where the rejection happened +# +# Example +# ------- +# exceptions: +# - rule: heading-case +# scope: "docs/contribute/style-guide.md" +# match: "H1 - Create the best documentation" +# reason: The style guide intentionally shows every heading level. +# added_by: someone +# added_on: 2026-01-01 +# source_pr: https://github.com/OpenFn/docs/pull/000 + +exceptions: [] diff --git a/translation-rules.yml b/translation-rules.yml new file mode 100644 index 000000000000..a28e55ad3ab6 --- /dev/null +++ b/translation-rules.yml @@ -0,0 +1,59 @@ +# translation-rules.yml +# +# Purpose +# ------- +# Locale-specific phrasing rules learned from human edits to machine +# translations. The translate skill (.agents/skills/translate.md) loads this +# file after glossary.yml and applies every rule whose `locale` matches the +# target locale. +# +# Glossary terms (never translate) belong in glossary.yml, not here. This file +# is for how to translate, not what to leave alone. +# +# Entries are added by the corrections-capture skill +# (.agents/skills/corrections-capture.md) when a reviewer edits a translated +# page and the edit implies a general pattern, or by humans directly. +# +# Schema +# ------ +# rules: +# - locale: string # "es" or "fr" (or "*" for every locale) +# kind: string # one of: +# # term - a fixed rendering for a phrase +# # register - tone/voice guidance (formal vs informal "you") +# # punctuation - spacing, quotation marks, list punctuation +# # structure - how to handle headings, admonition titles, UI labels +# # avoid - a rendering the reviewer rejected +# source: string # English phrase or pattern the rule applies to (for +# # kind: term and avoid). Omit for global rules. +# target: string # required rendering (kind: term) or rejected rendering +# # (kind: avoid) +# instruction: string # plain-language rule the translator must follow +# example_source: string # optional English example +# example_target: string # optional translated example +# reason: string # why, in one sentence +# added_by: string # GitHub handle +# added_on: string # ISO date (YYYY-MM-DD) +# source_pr: string # PR URL or number where the edit happened +# +# Example +# ------- +# rules: +# - locale: es +# kind: register +# instruction: Address the reader as "tú", not "usted". +# reason: Matches the informal tone of the English docs. +# added_by: someone +# added_on: 2026-01-01 +# source_pr: https://github.com/OpenFn/docs/pull/000 +# - locale: fr +# kind: term +# source: "Save and Run" +# target: "Enregistrer et exécuter" +# instruction: UI button labels are translated to match the French UI strings in OpenFn/lightning. +# reason: Reviewer aligned button names with the app's own translations. +# added_by: someone +# added_on: 2026-01-01 +# source_pr: https://github.com/OpenFn/docs/pull/000 + +rules: [] From aaeaf7d5fe1d399cf05eebf57c1f89b726b486e0 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 08:52:15 +0000 Subject: [PATCH 02/13] Cut skill and orchestrator length by more than half Keeps every rule from the spec; drops long command snippets, exhaustive path tables, and repeated output templates so the files are easier to read and maintain. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/accuracy-check.md | 260 ++++++--------------- .agents/skills/corrections-capture.md | 211 ++++------------- .agents/skills/fresh-user-eval.md | 172 +++++--------- .agents/skills/gap-analysis.md | 221 +++++------------- .agents/skills/lint.md | 289 +++++------------------ .agents/skills/screenshot-triage.md | 232 +++++-------------- .agents/skills/translate.md | 318 +++++++------------------- AGENTS.md | 289 ++++++++--------------- 8 files changed, 505 insertions(+), 1487 deletions(-) diff --git a/.agents/skills/accuracy-check.md b/.agents/skills/accuracy-check.md index 247941a75b5f..6af77bf33527 100644 --- a/.agents/skills/accuracy-check.md +++ b/.agents/skills/accuracy-check.md @@ -1,216 +1,84 @@ -# Skill: Accuracy verification +# Skill: Accuracy check Extract every verifiable claim from a docs page and check it against the code -that implements it. The docs describe three products in three repos; you must -look in the right one. +that implements it. ## Inputs -- The page (markdown source) or a section of pages. -- Read access to the product repos. Clone them into a scratch directory, not - into this repo: - - ```bash - SCRATCH=${SCRATCH:-/tmp/openfn-src} - mkdir -p "$SCRATCH" - git clone --depth 50 https://github.com/OpenFn/lightning "$SCRATCH/lightning" - git clone --depth 50 https://github.com/OpenFn/kit "$SCRATCH/kit" - git clone --depth 50 https://github.com/OpenFn/adaptors "$SCRATCH/adaptors" - ``` - - Check out the tag that matches what the docs describe when the page names a - version. Otherwise use the default branch, and say so in the output. - -## Where to look - -| Claim is about | Repo | Start here | -| ----------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------- | -| Web app UI, triggers, credentials, projects, runs, history, permissions, limits, API endpoints | `OpenFn/lightning` | `lib/lightning_web/router.ex` for routes; `lib/lightning_web/live/` for pages; `lib/lightning/` for domain logic; `config/runtime.exs` for env vars; `priv/repo/migrations/` for schema | -| Webhook auth, provisioning API, workflows API | `OpenFn/lightning` | `lib/lightning_web/controllers/api/` (`provisioning_controller.ex`, `workflows_controller.ex`, `run_controller.ex`, ...), `lib/lightning_web/controllers/webhooks_controller.ex`, `lib/lightning/workflows/webhook_auth_method.ex` | -| CLI commands and flags | `OpenFn/kit` | `packages/cli/src/commands.ts` (command list), `packages/cli/src/<command>/` (one dir per command: `deploy`, `execute`, `pull`, `docs`, `collections`, `projects`, ...), `packages/cli/src/options.ts`, `packages/cli/README.md` | -| `openfn deploy`, `project.yaml`, project spec format | `OpenFn/kit` | `packages/deploy/src/`, `packages/project/src/` | -| Job syntax, `state`, `$` lazy operator, `fn()`, `each()`, cursors, compilation | `OpenFn/kit` | `packages/compiler/`, `packages/runtime/`; common operations are in `OpenFn/adaptors` at `packages/common/src/` | -| Adaptor functions, configuration schema, versions | `OpenFn/adaptors` | `packages/<name>/src/Adaptor.js` (JSDoc), `packages/<name>/configuration-schema.json`, `packages/<name>/CHANGELOG.md`, `packages/<name>/package.json` | -| Docs site itself (build commands, contributing) | this repo | `package.json`, `README.md`, `.github/workflows/` | - -If a claim is about hosted-plan pricing, limits that are set per deployment, -roadmap, or policy, it is not verifiable from code. Mark it **uncertain** and -name Brandon's team (product) as the owner in the output. Do not guess. +- The page(s) to check. +- Read-only clones of the product repos in a scratch directory (never inside + this repo): -## Process - -### 1. Extract claims - -Read the page once and list every statement a reader could act on and be -wrong about. Number them. Categories: - -- **Code sample**: any fenced block the reader is meant to run or copy. -- **API behaviour**: a function, operation, endpoint, event, or command exists - and behaves as described (signature, arguments, defaults, return shape, - error behaviour). -- **UI behaviour**: a button, menu, page, or setting exists with the stated - name and does the stated thing. -- **Configuration**: an option, env var, front matter key, `project.yaml` - field, or credential schema key exists with the stated name, type, default, - and constraints. -- **Version**: a version number, "since v2.x", "as of", "latest", or a - compatibility statement. -- **Path or URL**: a file path, route, or link that must exist. -- **Quantity**: a limit, timeout, size, retention period, count. - -Skip marketing sentences, motivation, and analogies. Skip claims inside -`:::note In OpenFn V1 ...` history callouts unless the page is a migration -page. - -### 2. Check each claim - -For each claim, find the implementing code and record `repo/path:line`. - -**Code samples** - -- Job expressions (JavaScript using adaptor operations): run them with the - CLI against a stub state to confirm they compile and the named operations - exist. - - ```bash - cd "$SCRATCH" && npm i -g @openfn/cli - echo '{"configuration":{}, "data":{}}' > state.json - openfn compile sample.js -a <adaptor> # must compile - openfn sample.js -a <adaptor> -s state.json # runs if no network call - ``` - - Where a sample needs a live system, stop at `compile` and check each - operation's signature in the adaptor JSDoc. Confirm the described output - matches the return shape in the code. - -- Shell commands: run them where safe (`openfn --help`, `openfn <cmd> --help`) - and compare flags to the page. -- JSON/YAML config: validate against the schema (`configuration-schema.json` - for credentials; the `project.yaml` schema in `packages/deploy` for - projects). -- Do not run anything that deploys, deletes, sends email, or contacts a live - OpenFn instance. - -**API and UI behaviour** - -- Grep for the exact name the docs use. If it is not found, grep for - synonyms and look at recent commits (`git log -S '<name>'`) to see whether - it was renamed. A rename is a **fix** if the new name is unambiguous; - otherwise a **question**. -- For Lightning UI text, search `priv/gettext/` and `lib/lightning_web/` for - the label string. Button labels and menu names in the docs must match the - code exactly, including capitalisation. -- For routes, confirm in `router.ex`. - -**Configuration options** - -- Env vars: `grep -rn "System.get_env(\"NAME\"" config/ lib/` in Lightning, - or `process.env.NAME` in kit. Compare defaults. -- Confirm every option listed in a table exists, and note options that exist - in code but are missing from the table (report those to the gap analysis - skill, not as accuracy failures). - -**Versions** - -- Compare against `package.json` (kit, adaptors) or `mix.exs` (Lightning) on - the default branch, and against the latest git tag. A doc that says - "latest" and gives a stale number is a **fix** only if the page is about - installing that version; otherwise a **suggestion** to remove the number. - -**Quantities** - -- Find the constant or config value. Values that are set per deployment - (rate limits, retention, payload size) are **uncertain** unless the docs - page is explicitly about the hosted app and the value is in - `config/runtime.exs` defaults. - -### 3. Classify - -- **pass**: code matches the docs. -- **fail**: code contradicts the docs. Record expected (docs) vs actual - (code, with path and line). -- **uncertain**: could not find the implementing code, or the behaviour is - deployment-specific, or requires a live system to verify. + | Claim is about | Repo | Start in | + | --------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------- | + | Web app UI, triggers, credentials, runs, projects, API | `OpenFn/lightning` | `lib/lightning_web/router.ex`, `lib/lightning_web/live/`, `config/runtime.exs` | + | CLI commands and flags, `openfn deploy`, `project.yaml` | `OpenFn/kit` | `packages/cli/src/`, `packages/deploy/`, `packages/project/` | + | Job syntax, `state`, `fn()`, `each()`, `$` operator | `OpenFn/kit` and `OpenFn/adaptors` | `packages/compiler/`, `packages/runtime/`; `packages/common/src/` in adaptors | + | Adaptor functions, config schema, versions | `OpenFn/adaptors` | `packages/<name>/src/Adaptor.js`, `configuration-schema.json`, `package.json` | -Then, for each **fail**, decide the action: +Use the default branch unless the page names a version. Say which commit you +checked in the output. -- **fix** when the correct value is in the code and the surrounding sentence - still makes sense after the change (a flag name, a default, a menu label, a - version, a path). -- **suggestion** when fixing requires rewriting a paragraph or the docs might - be describing intended-but-unshipped behaviour. -- **question** when the docs and code disagree and either could be the bug. - Say which you suspect and why. - -## Generated adaptor reference pages - -Pages under `adaptors/packages/` (`<name>-docs`, `<name>-configuration-schema`, -`<name>-changelog`, `<name>-readme`) are rendered at build time from JSDoc in -`OpenFn/adaptors`. Never edit them here. - -When a claim on a generated page fails: - -1. Locate the JSDoc block in `$SCRATCH/adaptors/packages/<name>/src/` (usually - `Adaptor.js`). The function name in the page heading is the JSDoc - `@function` or export name. Configuration pages come from - `packages/<name>/configuration-schema.json`. -2. Draft an issue body in this exact shape and put it in the output under - "Upstream issues": - - ```markdown - ## <name> adaptor: docs for `<function>()` do not match behaviour - - **Page:** https://docs.openfn.org/adaptors/packages/<name>-docs#<anchor> - **Source:** `packages/<name>/src/Adaptor.js` L<line> (JSDoc for `<function>`) - **Adaptor version:** <from packages/<name>/package.json> - - ### What the docs say - - <quoted JSDoc text or rendered docs text> - - ### What the code does - - <one or two sentences, with the line reference> - - ### Suggested JSDoc change - - ```js - /** - * <corrected JSDoc> - */ - ``` - - Found by the docs accuracy check while reviewing <docs section>. - ``` +## Process -3. Also check whether a hand-written overview page `adaptors/<name>.md` - repeats the same wrong claim. If it does, that copy is editable here and - is a normal **fix**. +1. **List claims.** Read the page once and number every statement a reader + could act on and be wrong about: code samples, function or endpoint + signatures, UI labels and menu paths, config options and defaults, version + numbers, file paths, limits and timeouts. Skip motivation, analogies, and + v1 history callouts. +2. **Check each claim** and record `repo/path:line` as evidence. + - Code samples: compile job code with `openfn compile <file> -a <adaptor>`; + run shell commands where they are read-only (`--help`). Never run anything + that deploys, deletes, sends, or touches a live OpenFn instance. + - Names and labels: grep for the exact string. If missing, check + `git log -S '<name>'` for a rename. + - Config: find the constant or `System.get_env` / `process.env` read and + compare the default. + - Versions: compare against `package.json` or `mix.exs` and the latest tag. +3. **Classify** each claim as pass, fail, or uncertain. Uncertain covers + deployment-specific values (limits, retention), anything that needs a live + system, and product policy or pricing (not verifiable from code; the owner + is the product team). +4. **Decide the action** for each failure: + - *Fix* when the correct value is in the code and slots into the existing + sentence (a flag, default, label, version, path). + - *Suggestion* when the fix needs a rewritten paragraph or the docs may + describe intended behaviour. + - *Question* when docs and code disagree and either could be the bug. Say + which you suspect. + +## Generated adaptor pages + +Pages under `adaptors/packages/` are rendered from JSDoc in `OpenFn/adaptors`. +Never edit them here. For each failure, find the JSDoc block in +`packages/<name>/src/` and draft an issue: + +```markdown +## <name>: docs for `<function>()` do not match behaviour + +Page: https://docs.openfn.org/adaptors/packages/<name>-docs#<anchor> +Source: packages/<name>/src/Adaptor.js L<line> +Docs say: <quote> +Code does: <one sentence, with line reference> +Suggested JSDoc: <corrected block> +``` -Do not file the issue unless the user has asked you to file issues. Draft -only. +Put drafts under "Upstream issues" in the PR. File them only if the user asked +you to. If a hand-written overview `adaptors/<name>.md` repeats the same +error, that copy is a normal fix. ## Output ``` -Page: docs/<path>.md -Repo(s) checked: lightning@<sha>, kit@<sha>, adaptors@<sha> -Claims: N checked. Pass: N. Fail: N. Uncertain: N. +Page: docs/<path>.md — checked against lightning@<sha>, kit@<sha>, adaptors@<sha> +Claims: N. Pass: N. Fail: N. Uncertain: N. -| # | Claim (short) | Category | Result | Evidence | -|---|---------------|----------|--------|----------| -| 1 | `openfn deploy` reads `project.yaml` by default | config | pass | kit/packages/deploy/src/index.ts:42 | -| 2 | Retention default is 7 days | quantity | uncertain | deployment-specific; config/runtime.exs:210 has no default | +| # | Claim | Result | Evidence | +|---|-------|--------|----------| -Failures: -[fix] docs/<path>.md:L<line> — docs say "<expected>"; code does "<actual>" (repo/path:line) — changed to "<new text>" +[fix] docs/<path>.md:<line> — docs say X, code does Y (repo/path:line) — changed to Z [suggestion] ... [question] ... - -Upstream issues (OpenFn/adaptors): -<draft bodies> ``` -Apply the fixes, run Prettier on changed files, and confirm `yarn build` -still passes. Hand suggestions, questions, and upstream drafts to the PR -description. +Apply fixes, run Prettier, confirm `yarn build` passes. diff --git a/.agents/skills/corrections-capture.md b/.agents/skills/corrections-capture.md index a8ef8515dadd..3e144cf48c8e 100644 --- a/.agents/skills/corrections-capture.md +++ b/.agents/skills/corrections-capture.md @@ -1,179 +1,56 @@ # Skill: Corrections capture -When a human overrides something the agent produced, turn the override into a -rule so the same correction never has to be made twice. Capture the general -pattern, not the specific fix. +When a human overrides agent output, turn the override into a rule so the same +correction is never needed twice. Capture the pattern, not the one-off. ## Triggers -Run this skill when any of these happen: +| A human... | Rule file | +| --------------------------------------------------- | -------------------------------------------- | +| edits a machine-translated page | `glossary.yml` or `translation-rules.yml` | +| marks a translation `human-reviewed` | `translation-rules.yml` (mine the diff) | +| reverts or rejects a lint fix or suggestion | `style-exceptions.yml` | +| rewrites a section the fresh-user eval flagged | `style-exceptions.yml`, if the pattern should not be flagged again | +| reverts an accuracy fix | No rule. Raise a *question* for the product team. | -| Trigger | Where you see it | Likely rule file | -| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------- | -| A human edits a machine-translated page | A commit or PR touching `i18n/**` by a human author, or a review comment changing translated text | `glossary.yml` or `translation-rules.yml` | -| A human sets `translation_review_status: human-reviewed` | Front matter change in `i18n/**` | `translation-rules.yml` (mine the diff between machine and reviewed versions) | -| A human reverts or rejects a lint fix | A review comment on a lint commit, a follow-up commit undoing it, or a "won't fix" on a suggestion | `style-exceptions.yml` | -| A human rewrites a section the fresh-user eval flagged | A commit changing lines the eval reported, in a way different from the eval's proposal | `style-exceptions.yml` (if the eval's pattern should not be flagged) or a note in the PR | -| A human rejects an accuracy fix | Revert or review comment | Usually a **question** for the product owner, not a rule | - -## Inputs - -- The agent's original output: the commit, PR diff, or finding text. -- The human's override: the later commit, the review comment, or the edited - file. -- The three rule files at the repo root. - -Find candidates with: - -```bash -# Human commits to translated pages since the last agent commit -git log --format='%H %an %s' --no-merges -- i18n/ | grep -v -i 'docs-agent\|translate(' | head - -# Reverts of agent commits -git log --format='%H %s' --grep='Revert' --grep='revert' -i | head - -# PR review threads: use the GitHub tools to list review comments on the -# agent's PRs and filter for "changes requested" or comments on lines the -# agent changed. -``` +Find candidates with `git log --no-merges -- i18n/` filtered to human +authors, `git log --grep=revert -i`, and review comments on the agent's PRs. ## Process -### 1. Pair the agent output with the human edit - -For each candidate, produce a minimal before/after: - -```bash -git diff <agent-commit> <human-commit> -- <file> -``` - -Discard pairs where the human change is unrelated to what the agent did -(a new paragraph, an unrelated typo fix). - -### 2. Extract the general rule - -Ask, in order: - -1. **Is it terminology?** The human replaced one word or short phrase with - another, and the same replacement would apply anywhere the phrase occurs. - - In an English page: add or extend a `glossary.yml` entry. If the human - restored a spelling the lint changed, the lint's target was wrong; - correct `variants` and `term` accordingly. - - In a translated page where the human restored an English term: add the - term to `glossary.yml` with `translate: false`. - - In a translated page where the human changed how a phrase is rendered - and the phrase is not a product term: add a `translation-rules.yml` - entry with `kind: term`. - -2. **Is it a translation pattern?** The change is about register (tú/usted, - vous/tu), punctuation, how UI labels are rendered, how admonition titles - are handled, sentence structure, or a rendering to avoid. Add a - `translation-rules.yml` entry with the matching `kind`. Write the - `instruction` so a translator who has never seen the example would apply - it correctly. - -3. **Is it a rejected lint finding?** The human undid a lint change or said - "no" to a suggestion. Add a `style-exceptions.yml` entry. Scope it as - narrowly as the evidence supports: one page if the rejection was about - that page's content, a directory if the reviewer said "we do this - throughout X", the whole repo only if they said so. - -4. **Is it a style or phrasing preference** that is not a lint rule (the - human prefers "select" to "click", or wants imperative headings)? Add it - to `style-exceptions.yml` under the rule id `terminology` or - `heading-case` with a `match`, and also record the preferred form in - `glossary.yml` if it is a word-level preference. If it fits neither - schema, add a `# NOTE:` comment at the top of `style-exceptions.yml` - describing the preference and open a **question** to extend the schema. - -5. **Is it a disagreement about facts** (the human reverted an accuracy - fix)? Do not write a rule. Record a **question** in the PR: "Accuracy - check found X at repo/path:line, reviewer restored Y. Which is right?" - Product questions go to Brandon's team. - -A single human edit can yield more than one rule. A single rule should cover -every future instance of the pattern, so prefer "UI button labels match the -French UI strings in OpenFn/lightning" over "Save and Run → Enregistrer et -exécuter" alone. Record the specific example in `example_source` and -`example_target` and the general rule in `instruction`. - -### 3. Write the rule - -Append to the relevant file following its header schema. Always fill in: - -- `reason`: one sentence, in your words, of why the human made the change. - If the human left a review comment, quote it. -- `added_by`: the human's GitHub handle (the person whose edit you are - capturing, not you). -- `added_on`: today's date, ISO format. -- `source_pr`: the PR or commit where the override happened. - -Do not add PII beyond a GitHub handle. Do not paste support-ticket text. - -Check the file still parses: - -```bash -node -e 'const y=require("js-yaml"); for (const f of ["glossary.yml","style-exceptions.yml","translation-rules.yml"]) y.load(require("fs").readFileSync(f,"utf8")); console.log("ok")' -``` - -(`js-yaml` ships with Docusaurus, so it is in `node_modules`.) - -### 4. Check for conflicts - -- A new `glossary.yml` entry must not duplicate an existing `term` or - appear in another entry's `variants`. Merge instead. -- A new `style-exceptions.yml` entry must not silence a rule so broadly that - the lint stops working (`rule: internal-link`, `scope: "**"`, no `match` - is never acceptable). If the human asked for that, record it as a - **question**. -- A new `translation-rules.yml` entry must not contradict an existing rule - for the same locale and `source`. If it does, keep the newer one and - record the conflict in the PR for the reviewer to confirm. - -### 5. Apply retroactively where cheap - -- New glossary term: grep `docs/` for variants and fix them (that is a normal - lint fix). Grep `i18n/` for translated occurrences of a now-protected term - and list them under "Also touched" or as a follow-up if the count is - large. -- New style exception: nothing to apply. -- New translation rule: do not retranslate other pages now. The next - translate run will pick up the rule. - -### 6. Open a PR - -One PR per capture run, titled `rules: capture corrections from <PR or -commit>`. Body: - -```markdown -## What was overridden - -- <agent commit or PR> changed <file> L<line>: "<agent text>" -- <human> changed it to: "<human text>" (<commit or review link>) - -## Rules added - -- glossary.yml: `<term>` ... -- translation-rules.yml: <locale> <kind> "<instruction>" -- style-exceptions.yml: <rule> on <scope> — <reason> - -## Retroactive changes - -- <files touched, if any> - -## Questions - -- <anything that looked like a factual disagreement> -``` - -Tick "I have used Claude Code" in the AI Usage section of the PR template. -Rules take effect only after this PR is merged; do not rely on them in the -same run. +1. **Pair** the agent's change with the human's change + (`git diff <agent-commit> <human-commit> -- <file>`). Discard pairs where + the human change is unrelated. +2. **Extract the rule.** Ask in order: + - Terminology? A word or phrase replaced in a way that applies everywhere. + English page: add or correct a `glossary.yml` entry. Translated page + where an English term was restored: add it with `translate: false`. + Translated page where a non-product phrase was re-rendered: a + `translation-rules.yml` entry with `kind: term`. + - Translation pattern? Register, punctuation, UI label handling, a + rendering to avoid: `translation-rules.yml` with the matching `kind`. + Write the `instruction` so it applies without seeing the example. + - Rejected lint finding? `style-exceptions.yml`, scoped as narrowly as the + evidence supports: one page unless the reviewer said otherwise. + - Factual disagreement? No rule. Record a *question*. + + If you cannot state the rule so that it applies to at least one other + page, skip it. +3. **Write the rule** following the file's header schema. Fill `reason` + (quote the review comment if there is one), `added_by` (the human's + GitHub handle), `added_on`, and `source_pr`. No other personal data. +4. **Check for conflicts.** No duplicate glossary terms. No exception broad + enough to disable a rule repo-wide. No contradictory translation rules + for the same locale and phrase; keep the newer and flag the conflict. +5. **Apply retroactively where cheap.** New glossary variant: fix it across + `docs/`. New translation rule: leave other pages for the next translate + run. +6. **Open a PR** titled `rules: capture corrections from <PR or commit>` + listing what was overridden, the rules added, and any questions. Rules + take effect only after merge. ## Do not -- Do not capture a one-off (a typo the human fixed, a rewording specific to - one sentence) as a rule. If you cannot state the rule in a form that - would apply to at least one other page, skip it. -- Do not edit the human's change itself. -- Do not mark anything `human-reviewed`. Only humans set that field. +- Capture typos or one-sentence rewordings as rules. +- Edit the human's change. +- Set `human-reviewed` on anything. diff --git a/.agents/skills/fresh-user-eval.md b/.agents/skills/fresh-user-eval.md index cd4bc26726b4..7422d82c0fec 100644 --- a/.agents/skills/fresh-user-eval.md +++ b/.agents/skills/fresh-user-eval.md @@ -1,143 +1,81 @@ # Skill: Fresh-user evaluation -Read a docs page the way a new user would, with no prior knowledge of OpenFn, -and try to do what the page says. Report every place you had to guess. - -This skill is about the reader's experience, not correctness. Correctness is -the accuracy-check skill (`.agents/skills/accuracy-check.md`). Run this skill -after accuracy so you are evaluating a page whose claims are already known -to be true. +Read a page as a new user would and try to do what it says. Report every place +you had to guess. Run this after the accuracy check so you are evaluating a +page whose facts are already right. ## Inputs -- One docs page (markdown source). Evaluate one page at a time even when the - section has many. -- Read access to the relevant product repo(s) for verifying that what you - eventually figured out is actually right. Use the repo map in - `.agents/skills/accuracy-check.md` to find the right one. -- Nothing else. In particular, do not read neighbouring pages during the - first pass. +- One page at a time. +- Read access to the product repos, only for pass 3. ## Process -### Pass 1: read cold - -1. Clear your assumptions. You know what a webhook, an API, JSON, and a - terminal are. You do not know what a Step, a work order, a dataclip, an - adaptor, or the Canvas is unless this page tells you. -2. Read the page top to bottom once, without following any links. -3. Write down, in one sentence, what task or concept the page is teaching. - If you cannot, that is your first finding. -4. Write down who the page seems to be for (non-technical project manager, - implementer building a workflow in the web app, developer using the CLI, - self-hoster). If the page switches audience halfway, note where. - -### Pass 2: attempt the task - -Follow the page as literally as you can. - -- For procedural pages ("Configure a Step", "Deploy with the CLI"): perform - each step. Where it needs the web app, walk the route in the Lightning - source (`lib/lightning_web/live/`) or use a local instance if one is - available, and confirm the named button or menu exists where the page says. - Where it needs the CLI, run the commands. -- For conceptual pages ("State", "Key Concepts"): after reading, try to - explain the concept back in two sentences and then answer three questions a - new user would ask. If you cannot answer them from the page, record what is - missing. -- For reference pages (tables of options, status codes): pick three entries - and check you could use each one from the description alone. - -At every point where you had to stop, record a finding. Triggers: - -- **Guess**: the page uses a term, path, or name it never defined, and you - had to infer it. Record the term and what you inferred. -- **Stuck**: the next step depends on something the page did not tell you - (where a button is, what value to enter, which page to be on first). -- **Ambiguity**: a sentence has two readings and they lead to different - actions. -- **Missing prerequisite**: you needed an account, a credential, an installed - tool, or an earlier setup step the page assumes. -- **Order**: the steps are listed in an order that does not work if followed - literally. -- **Unverifiable outcome**: the page tells you to do something but not what - success looks like. -- **Dead end**: a link you needed to follow to continue (after Pass 1 you may - follow links) went to a page that does not answer the question. - -### Pass 3: verify your guesses - -For each Guess and Ambiguity, check the code or a neighbouring page to learn -the right answer. Record whether your guess was right. A wrong guess is -strong evidence the page needs the information; a right guess is weaker but -still a finding. - -## Classify findings - -- **fix**: the missing information is a single fact you have now verified - (the menu path, the default value, the prerequisite command), and it fits in - one sentence or one list item at a specific line. Apply it. -- **suggestion**: the page needs restructuring, a new subsection, an example, - a screenshot, or a rewrite of more than a couple of sentences. Propose the - text in the PR description. Do not apply. -- **question**: you could not determine the right answer from code, or the - fix depends on which audience the page is for. Ask. - -Do not "fix" tone or voice. Do not add content beyond what the finding needs. +**Pass 1: read cold.** Assume you know what webhooks, APIs, JSON, and a +terminal are, and nothing about OpenFn. Read the page once without following +links. Write down in one sentence what it teaches and who it is for. If you +cannot, that is your first finding. -## Scores +**Pass 2: do the task.** Follow the page literally. + +- Procedural pages: perform each step. For web app steps, confirm the named + button or page exists in `OpenFn/lightning` (`lib/lightning_web/live/`). + For CLI steps, run the commands. +- Conceptual pages: explain the concept back in two sentences, then answer + three questions a new user would ask, using only the page. +- Reference pages: pick three entries and check you could use each from its + description alone. + +Record a finding each time you: had to guess a term or path the page never +defined; got stuck because a step depends on something the page did not say; +hit a sentence with two readings; needed a prerequisite the page assumes; +found steps in an order that does not work; could not tell what success looks +like. -Give two scores after the findings. Whole numbers only. +**Pass 3: verify your guesses.** Check the code or neighbouring pages. A +wrong guess is strong evidence the page needs the information. A right guess +is still a finding. -**Readability (1 to 5)**: how easy the prose was to follow on the first pass. +## Classify -- 5: read it once, understood everything, no re-reading. -- 4: one or two sentences needed re-reading; terminology mostly defined. -- 3: understood the gist but several undefined terms or long detours. -- 2: had to reconstruct the meaning from context repeatedly. -- 1: could not tell what the page was about without outside knowledge. +- *Fix*: a single verified fact that fits in one sentence at a specific line. +- *Suggestion*: a new subsection, example, screenshot, or rewrite of more + than a couple of sentences. Propose the text; do not apply. +- *Question*: you could not find the answer, or the fix depends on the + intended audience. -**Completeness (1 to 5)**: could a new user accomplish the task with only -this page? +Do not fix tone or voice. Do not add beyond what the finding needs. -- 5: yes, start to finish, including knowing when they are done. -- 4: yes, with one small guess that turned out right. -- 3: yes, but only after following links or guessing more than once. -- 2: no, a required step, prerequisite, or value is missing. -- 1: no, the page does not actually describe how to do the task. +## Scores + +**Readability (1 to 5)**: 5 means understood on one read; 3 means got the +gist despite undefined terms; 1 means unintelligible without outside +knowledge. -Justify each score in one sentence that names the specific thing that cost -points. +**Completeness (1 to 5)**: could a new user finish the task with only this +page? 5 yes, including knowing they are done; 3 yes after following links or +guessing more than once; 1 the page does not describe how to do the task. + +One sentence per score naming what cost points. ## Output ``` Page: docs/<path>.md -Teaches: <one sentence> -Audience: <one phrase>; switches at L<line> if applicable -Attempted: <what you did, two or three sentences> +Teaches: <one sentence>. Audience: <phrase>. +Attempted: <two sentences> -Findings: -[fix] docs/<path>.md:L<line> — guess — "<term>" never defined; inferred "<meaning>"; verified at <repo/path:line> — added "<sentence>" -[suggestion] docs/<path>.md:L<line> — stuck — step 4 says "select the credential" but no credential exists yet — propose inserting a "Before you begin" list: ... -[question] docs/<path>.md:L<line> — ambiguity — "the run" could mean the Run or the manual run button — which? +[fix] docs/<path>.md:<line> — <what was missing> — added "<text>" +[suggestion] docs/<path>.md:<line> — <what was missing> — propose <text> +[question] docs/<path>.md:<line> — <ambiguity> — <options> -Readability: N/5 — <one sentence> -Completeness: N/5 — <one sentence> +Readability: N/5 — <why> +Completeness: N/5 — <why> ``` -Apply fixes, run Prettier on changed files, and confirm `yarn build` passes. -Put scores in the PR "Scores" table, suggestions and questions in their -sections. - ## Do not -- Do not read the page's git history or the PR that introduced it before - Pass 1. That is prior context a user would not have. -- Do not evaluate generated adaptor reference pages (`adaptors/packages/**`). - Their readability is a JSDoc concern; if a hand-written overview page - (`adaptors/<name>.md`) exists, evaluate that instead. -- Do not edit pages with `translation_review_status: human-reviewed`. Record - suggested diffs. -- Do not write a new page. If the task cannot be done because the page does - not exist, that is a finding for the gap analysis skill. +- Read the page's git history before pass 1. +- Evaluate generated adaptor pages. Evaluate `adaptors/<name>.md` instead. +- Edit human-reviewed translations. Suggested diffs only. +- Write a new page. A task with no page is a gap for `gap-analysis.md`. diff --git a/.agents/skills/gap-analysis.md b/.agents/skills/gap-analysis.md index 0eb1b3d8d8aa..f6bc9bef7d6d 100644 --- a/.agents/skills/gap-analysis.md +++ b/.agents/skills/gap-analysis.md @@ -2,186 +2,79 @@ Compare what a docs section covers against what exists in the product and what users ask about. Produce a ranked list of gaps. Do not write the missing -pages unless the user asked for them. +pages unless asked. ## Inputs -- A section: a sidebar category from `sidebars-main.js` or a directory under - `docs/`. -- The product repos, cloned to a scratch directory (see the setup block in - `.agents/skills/accuracy-check.md`). -- Optional, only if the session has access: support channels and search - analytics (see "Optional sources"). Never assume access; check, and say in - the output which sources you used. +- A section (sidebar category or `docs/` directory). +- Read-only clones of the product repos (see the table in + `accuracy-check.md` for where things live). +- Optional: GitHub issues on `OpenFn/docs`, the community forum + (community.openfn.org), support channels, and search analytics. Use only + what the session actually has access to, and say which in the output. Never + invent user demand. ## Process -### 1. Inventory what the docs cover +1. **Inventory the docs.** For each page, list what it covers using headings + and tables. Note links out of the section: those are things it assumes are + documented elsewhere. Check that they are. +2. **Inventory the product**, choosing what matches the section: + - Web app: routes in `router.ex`, LiveViews in `lib/lightning_web/live/`, + env vars in `config/runtime.exs`. + - CLI: `packages/cli/src/commands.ts` and `openfn --help`. + - Job writing: exports of `packages/common/src/` in adaptors, transforms in + `packages/compiler/` in kit. + - Deployment: `DEPLOYMENT.md`, `docker-compose.yml`, `config/runtime.exs`. + - Adaptors: hand-written overviews (`adaptors/<name>.md`) missing for + heavily used adaptors. +3. **Diff.** For each product item with no matching docs item, search the + whole `docs/`, `articles/`, and `adaptors/*.md` trees before calling it a + gap. Label each gap: + - **missing page**: nothing in the docs mentions it. + - **partial page**: the right page exists but does not cover this item. + - **misplaced**: documented, but not where a user on this task would look. + - **stale**: documented for an older version. Hand to `accuracy-check.md`. + + Also note docs items that no longer exist in the product. +4. **Check user signals** if available: issues, forum threads, or support + questions matching the section's terms. Count them; do not quote people. +5. **Rank** by scoring each gap 1 to 5 on reach (how many users hit it), + severity (what goes wrong without it), evidence (5 with repeated user asks, + 3 if prominent in the UI or CLI, 1 if only found in code), and effort + (5 if a paragraph fixes it, 1 if it needs a tutorial). Sum and sort. -For every page in the section, list the concepts, features, commands, -options, and endpoints it documents. Use the page's headings plus any tables. -Write this as a flat list of "covered items", each with the page and heading -where it lives. - -```bash -grep -n -E '^#{2,4} ' docs/<section>/*.md -``` - -Also list every internal link the section makes to pages outside the section. -Those are things the section assumes are documented elsewhere; check that -they actually are. - -### 2. Inventory what exists in the product - -Pick the inventory that matches the section. Do only the relevant ones. - -**Web app features (Platform sections)**: in `$SCRATCH/lightning` - -```bash -# routes = user-facing pages and API endpoints -grep -n -E 'live |get |post |put |patch |delete ' lib/lightning_web/router.ex -# LiveView modules = screens -ls lib/lightning_web/live/ -# feature flags and config toggles -grep -rn -E 'Application\.(get_env|fetch_env)' lib/ | grep -o -E ':[a-z_]+\]?' | sort -u -grep -n -E 'env!?\(' config/runtime.exs -``` - -**CLI (CLI section)**: in `$SCRATCH/kit` +## Output -```bash -grep -n -E "^\s+'?[a-z-]+'?" packages/cli/src/commands.ts | head -60 # command names -ls -d packages/cli/src/*/ # one dir per command -grep -rn -E "^\s+'?[a-z-]+'?:\s*\{" packages/cli/src/options.ts | head -100 -npx @openfn/cli --help ``` +Section: <name>, N pages. Product checked: lightning@<sha>, kit@<sha>. +Sources used: <list>. -**Job writing (Write Jobs section)**: in `$SCRATCH/adaptors/packages/common/src/` -for every exported operation, and `$SCRATCH/kit/packages/compiler/` for -syntax transformations (lazy `$` operator, `fn` wrapping, imports). - -**Deployment (Deployment section)**: `$SCRATCH/lightning/DEPLOYMENT.md`, -`config/runtime.exs` env vars, `docker-compose.yml`, and the Helm or -Kubernetes manifests if present. - -**Adaptors (Adaptors section)**: every `packages/<name>` in -`$SCRATCH/adaptors` should have a generated reference page. Hand-written -overview pages (`adaptors/<name>.md` in this repo) are optional; note which -of the twenty most downloaded adaptors (by `npm view @openfn/language-<name>` -or recent changelog activity) lack one. - -**Migration**: compare `docs/migration/` against the v1 to v2 rename list in -`docs/get-started/terminology.md` and the migration tooling in `kit`. - -### 3. Diff - -For each product item with no covered item that matches: - -- Check the whole `docs/` tree, `articles/`, and `adaptors/*.md` before - declaring a gap. A feature documented in another section is a - cross-linking gap ("page exists but this section does not point to it"), - not a missing page. -- Distinguish: - - **missing page**: nothing in the docs mentions the item. - - **partial page**: a page exists and is the right home, but does not - cover this item (a flag, an option, an edge case, an error). - - **misplaced**: the item is documented, but in a section a user on this - task would not look in. - - **stale**: the item is documented for a previous version and the current - behaviour is different (hand this to the accuracy check if it is not - already recorded there). - -Also record the inverse: covered items that no longer exist in the product. -Those are accuracy failures; note them and move on. - -### 4. Optional sources - -Use these only if the tools are present in the session and the user has -connected them. Never fabricate examples of user questions. - -- **Community forum** (https://community.openfn.org): if you have web access, - search the last twelve months for the section's key terms. Count threads - per topic. A topic with three or more threads and no docs page is a - high-impact gap. -- **Support inbox or Slack** (if a connector is present): search the same - terms. Record the count, never the content or names of people asking. -- **Search analytics** (Algolia dashboard for index `openfn`, if - accessible): queries with zero results or with high volume and low - click-through that contain the section's terms. -- **GitHub issues** on `OpenFn/docs` labelled as documentation requests: - always available via the GitHub tools. Search for the section's terms. - -If none of these are accessible, say so and rank on product evidence alone. - -### 5. Rank by user impact - -Score each gap 1 to 5 on each of: - -- **Reach**: how many users hit this. Core workflow features and the - getting-started path are 5; niche self-hosting flags are 1 or 2. -- **Severity**: what happens without the doc. Silent data loss or a security - misconfiguration is 5; slight inconvenience is 1. -- **Evidence**: 5 if support or forum data shows repeated asks; 3 if the - feature is prominent in the UI or CLI help; 1 if you only found it in code. -- **Effort**: inverted. 5 if a paragraph fixes it; 1 if a whole tutorial is - needed. - -Impact = Reach + Severity + Evidence + Effort. Sort descending. Ties go to -the one with higher Severity. +1. [missing page] <title> — 17/20 (reach 5, severity 4, evidence 4, effort 4) + Missing: <two sentences> + Evidence: <repo/path:line>; <issue or thread count> + Should live: docs/<dir>/<slug>.md, sidebar "<Category>" after "<page>" + Outline: <H2 list> -## Output +2. [partial page] docs/<path>.md lacks <item> — 14/20 (...) + Should live: new "## <heading>" after "## <existing heading>" + Outline: ... -``` -Section: <name>, N pages, M covered items -Product inventory: lightning@<sha> (R routes, L live views), kit@<sha> (C commands), ... -Sources used: product code; GitHub issues (N matched); forum (not accessible) ... - -Gaps (ranked): - -1. [missing page] <title of the missing thing> — impact 17/20 (reach 5, severity 4, evidence 4, effort 4) - What's missing: <two sentences> - Evidence: <repo/path:line>; <forum thread count or issue link> - Where it should live: docs/<dir>/<slug>.md, sidebar "<Category>" after "<existing page>" - Suggested outline: - - <H2> - - <H3> - - <H2> - -2. [partial page] docs/<path>.md does not cover <item> — impact 14/20 (...) - What's missing: ... - Evidence: ... - Where it should live: docs/<path>.md, new "## <heading>" after "## <existing heading>" - Suggested outline: ... - -3. [misplaced] ... - Where it is: docs/<path>.md#<anchor> - Where this section should link from: docs/<path>.md L<line> - -Covered items that no longer exist in the product: -- docs/<path>.md L<line>: <item> (removed in <repo> at <commit>) → handed to accuracy check +Docs items no longer in the product: <list, handed to accuracy check> ``` -Write the full list into the PR under "Gaps (ranked)". Cap the PR list at the -top ten and attach the rest as a collapsed `<details>` block. +Put the top ten in the PR under "Gaps (ranked)"; collapse the rest in a +`<details>` block. -## Actions you may take +## Actions -- **fix**: adding a cross-link to an existing page from the obvious place in - this section, when the target page clearly covers the item. One sentence, - one link. -- Everything else is a **suggestion**. Do not create pages, sections, or - sidebar entries unless the user asked for the gap to be filled. If they - did, write the page following the suggested outline, add it to - `sidebars-main.js`, and run `yarn build`. +- *Fix*: add a one-sentence cross-link when the target page clearly exists. +- Everything else is a *suggestion*. Create pages only if the user asked, and + then add them to `sidebars-main.js` and run `yarn build`. ## Do not -- Do not count generated adaptor reference pages as gaps in this repo. A - missing or thin adaptor function description is a JSDoc gap; note it as a - draft issue for `OpenFn/adaptors` under "Upstream issues". -- Do not invent user demand. If you have no support or analytics data, the - Evidence score maxes at 3. -- Do not propose documenting internal, experimental, or feature-flagged - behaviour. Check for a feature flag or `experimental` marker in the code - before listing a feature; if present, list it as a **question** ("Is X - meant to be public yet?") for Brandon's team rather than a gap. +- Count thin generated adaptor pages as gaps here; they are JSDoc issues for + `OpenFn/adaptors`. +- Propose documenting feature-flagged or experimental behaviour. Raise a + *question* for the product team instead. diff --git a/.agents/skills/lint.md b/.agents/skills/lint.md index c698893d1b2c..9290b736737e 100644 --- a/.agents/skills/lint.md +++ b/.agents/skills/lint.md @@ -1,254 +1,77 @@ -# Skill: Style and structure lint +# Skill: Lint -Deterministic checks on docs markdown. Most of this needs no judgement: run -the check, apply the fix, move on. Use this skill first on any section, before -accuracy, fresh-user, or translation work. +Deterministic style and structure checks on docs markdown. Run this first on +any section. Most findings are fixes you apply directly. ## Inputs -- A section: a sidebar category from `sidebars-main.js`, a directory under +- A section: a sidebar category in `sidebars-main.js`, a directory under `docs/`, or a single page. -- `glossary.yml` at the repo root (terminology rules). -- `style-exceptions.yml` at the repo root (findings humans have rejected). -- `sidebars-main.js` and `sidebars-adaptors.js` (for orphan detection). -- `docusaurus.config.js` (for redirects and the `onBrokenLinks: 'throw'` - setting). +- `glossary.yml` (terminology) and `style-exceptions.yml` (findings humans + have rejected). Load both before you start. Drop any finding that matches an + exception. -## Files you must not lint-fix +## Never lint-fix -- `adaptors/packages/**` and `adaptors/library/jobs/auto/**`: generated at - build time from `OpenFn/adaptors`. Not in git. Any issue there is an - upstream JSDoc issue; record it and move on. -- `versioned_docs/**`: frozen v1 docs. Record findings as suggestions only. -- Any page whose front matter contains - `translation_review_status: human-reviewed`. Record findings as a suggested - diff only. - -## Setup - -```bash -yarn install --immutable -``` - -Then, for each check, work from a file list: - -```bash -# Example: the "Write Jobs" section -FILES=$(node -e ' - const s = require("./sidebars-main.js").docs; - const walk = (items, label) => items.flatMap(i => - typeof i === "string" ? (label ? [i] : []) : - i.type === "category" ? walk(i.items, label || i.label === process.argv[1]) : []); - console.log(walk(s, false).map(id => `docs/${id}.md`).join("\n")); -' "Write Jobs") -``` - -Adjust the extension to `.mdx` where the file is MDX. If a sidebar id points to -a file that does not exist with either extension, that is a **fix**: correct -the id or restore the file. +- `adaptors/packages/**` and `adaptors/library/**`: generated from + `OpenFn/adaptors` at build time. Record as an upstream issue. +- `versioned_docs/**`: frozen v1 docs. Suggestions only. +- Pages with `translation_review_status: human-reviewed`. Suggested diff only. ## Checks -Run every check on every file in the section. Load `style-exceptions.yml` -first and drop any finding that matches an exception (`rule` equal, file -matches `scope` glob, and `match` substring or regex present in the finding -text when `match` is set). - -### 1. Terminology (`terminology`) - -For each entry in `glossary.yml`: - -- For every string in `variants`, search the prose (not code blocks, not - inline code, not URLs, not front matter) for a whole-word match. Replace - with `term`, preserving sentence-initial capitalisation and plural `s`. - This is a **fix**. -- If `case_sensitive: true`, also flag case variants of `term` (e.g. "openfn" - in prose). **Fix**. -- For entries with `product_noun: true`, do not flag ordinary-English use. - Only flag variants. - -Also flag "adapter" anywhere in prose regardless of glossary, since it is the -single most common error. **Fix**. - -Strip code before matching: - -```bash -# crude but reliable: remove fenced blocks and inline code, then grep -perl -0pe 's/```.*?```//gs; s/`[^`]*`//g' "$f" | grep -n -i -w -E 'adapter|open fn|workorder|data clip' -``` - -### 2. Heading hierarchy (`heading-hierarchy`, `heading-case`) - -- Pages must not contain an `# H1` heading in the body. The title comes from - front matter `title`. A body H1 is a **fix**: convert to `##` and shift its - children down one level, unless the front matter has no `title`, in which - case move the H1 text into `title:` and delete the heading. -- Heading levels must not skip (a `##` followed by `####`). **Fix** by - promoting the deeper heading. -- Heading case must be consistent within a page. The repo convention is - sentence case with product nouns capitalised ("Create or edit a Step"). - Flag a page that mixes Title Case and sentence case. Converting case is a - **suggestion**, not a fix, because headings are link anchors and changing - them can break inbound `#fragment` links. Exception: if you convert, grep - the repo for the old anchor first and update every reference in the same - change; then it is a **fix**. -- Duplicate heading text within one page produces duplicate anchors. **Fix** - by making the second unique, then update any in-page links to it. - -```bash -grep -n -E '^#{1,6} ' "$f" -``` - -### 3. Internal links (`internal-link`) - -Extract every `](...)` and `href="..."` target that starts with `/`, `./`, -`../`, or `#`. - -- Site-absolute links (`/documentation/...`, `/adaptors/...`, `/articles/...`) - must resolve to an existing page id or a redirect `from` in - `docusaurus.config.js`. Map `/documentation/<a>/<b>` to `docs/<a>/<b>.md` - or `.mdx`, honouring `slug:` and `id:` front matter overrides. -- Relative `.md` links must resolve on disk. -- `#fragment` links must match a heading in the target page after Docusaurus - slugification (lower case, spaces to `-`, punctuation removed). -- Links to `/adaptors/packages/...` cannot be checked without running - `yarn generate-adaptors`. Run it if network allows; otherwise mark - **uncertain** and list them in the PR. - -Broken internal links are a **fix** when the intended target is unambiguous -(one candidate page with matching title or slug). Otherwise a **question**. - -The fastest authoritative check is the build, because `onBrokenLinks` is -`throw`: - -```bash -yarn build 2>&1 | grep -A3 -i 'broken' -``` - -### 4. External links (`external-link`) - -For every `http://` or `https://` link outside code blocks: - -```bash -curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' -L --max-time 15 -A 'openfn-docs-lint' "$url" -``` - -- 404, 410, or connection failure on two tries five seconds apart: dead. - Replacing a dead link is a **fix** only when the page has an obvious - successor (a 301 target, or the same path on a renamed domain). Otherwise - a **suggestion**: propose removal or an archive.org link. -- 403 and 429 are not dead. Mark **uncertain** and skip. -- `http://` links that respond on `https://` are a **fix**: upgrade them. -- Do not check links inside code blocks (they are examples, not references). - -### 5. Orphaned pages (`orphan-page`) - -A page is orphaned when its id appears in no sidebar and no other page links -to it. - -```bash -# every doc id on disk -find docs -type f \( -name '*.md' -o -name '*.mdx' \) | sed -E 's|^docs/||; s|\.mdx?$||' | sort > /tmp/ids -# every id referenced in the sidebar -node -e 'console.log(JSON.stringify(require("./sidebars-main.js")))' | grep -o -E '"[a-z0-9/_.-]+"' | tr -d '"' | sort -u > /tmp/sidebar -comm -23 /tmp/ids /tmp/sidebar -``` - -Then, for each candidate, grep `docs/`, `articles/`, `adaptors/*.md`, and -`src/` for links to it. Honour `id:` and `slug:` overrides. A page that is -genuinely unreachable is a **suggestion** (add to sidebar, or delete), never a -fix: someone may be drafting it. Note in the PR whether the page looks -finished. - -Pages under `versioned_docs/` are checked against -`versioned_sidebars/version-legacy-sidebars.json`, and findings are -suggestions only. - -### 6. Front matter (`frontmatter`) - -Every page in `docs/` and `adaptors/*.md` must have YAML front matter with at -least `title`. **Fix** a missing `title` by using the body H1 (then remove the -H1) or, failing that, the file name in sentence case (record the invented -title as a **suggestion** so a human confirms it). - -Every page under `i18n/**` must have all five translation fields: -`translation_source_hash`, `translation_review_status`, `translation_model`, -and, when status is `human-reviewed`, `translation_reviewer` and -`translation_review_date`. A missing field on a translated page is a -**question** unless the translate skill is about to regenerate the page. - -Front matter must parse as YAML. Unquoted values containing `:` are the usual -cause of failure. **Fix** by quoting. - -### 7. Code block language tags (`code-language`) - -Every fenced code block must have a language after the opening fence. - -```bash -awk '/^```/{ if (open) { open=0 } else { open=1; if ($0 ~ /^```\s*$/) print FILENAME":"NR": untagged fence" } }' "$f" -``` - -Infer the language from content and add it. Use these tags: `js` for job -code and JavaScript, `json`, `yaml`, `bash` for shell commands, `text` for -console output, logs, and anything else. This is a **fix**. If you cannot -tell what the block is, tag it `text`. - -### 8. Image alt text (`image-alt`) - -Every `![...](...)` must have non-empty alt text that describes what the -image shows, not "image" or "screenshot". Every `<img>` must have a -non-empty `alt` attribute. - -- Empty alt: **fix**. Write alt text from the surrounding paragraph and the - file name (`anatomy_of_step.webp` next to "A Step includes these key - components" becomes "Diagram of a Step showing its name, adaptor, credential, - and job expression"). -- Alt text that is only "image", "screenshot", "img", or the file name: - **fix** the same way. -- Image path that does not exist under `static/` (for `/img/...` paths) or - relative to the page: **fix** if there is exactly one file with the same - base name under `static/img/`; otherwise **question**. - -### 9. Admonition spacing (`admonition-spacing`) - -`:::tip`, `:::note`, `:::info`, `:::warning`, `:::caution`, and `:::danger` -blocks need a blank line after the opening line and before the closing `:::`, -or MDX renders them wrong. **Fix**. +Prose only: skip code blocks, inline code, URLs, and front matter when +matching text. + +1. **Terminology.** Replace every `variants` spelling from `glossary.yml` + with its `term`, keeping capitalisation and plurals. Flag case variants + when `case_sensitive` is true. Skip ordinary-English uses of + `product_noun` terms. Always fix "adapter" → "adaptor". *Fix.* +2. **Heading hierarchy.** No H1 in the body (the title comes from front + matter). No skipped levels. No duplicate heading text in one page. *Fix.* + Mixed heading case within a page is a *suggestion*, because headings are + anchors; only convert if you also update every inbound `#fragment` link. +3. **Internal links.** Every `/documentation/...`, `/adaptors/...`, + `/articles/...`, relative `.md`, and `#fragment` link must resolve. The + build is authoritative: `onBrokenLinks` is `throw`, so run `yarn build`. + *Fix* when the intended target is unambiguous, otherwise *question*. +4. **External links.** Check each `http(s)` link with `curl -IL`. 404 or 410 + after two tries is dead: *fix* if there is an obvious successor, else + *suggestion*. 403 and 429 are not dead; mark uncertain. Upgrade `http://` + to `https://` where it works. +5. **Orphaned pages.** A page in `docs/` that appears in no sidebar and is + linked from no other page. *Suggestion* (add to sidebar or delete); never + auto-fix, someone may be drafting it. +6. **Front matter.** Every page needs valid YAML with at least `title`. + Translated pages also need the fields listed in `translate.md`. Missing + `title`: *fix* from the body H1. Unparseable YAML: *fix* by quoting. +7. **Code block language.** Every fence needs a tag. Use `js`, `json`, + `yaml`, `bash`, or `text`. *Fix.* +8. **Image alt text.** Every image needs alt text that says what it shows, + not "image" or the file name. Write it from the surrounding paragraph. + Missing image file: *fix* if exactly one match exists in `static/img/`, + else *question*. +9. **Admonitions.** `:::tip` and friends need a blank line inside both + fences. *Fix.* ## Applying fixes -- Apply fixes with minimal edits. Do not reflow paragraphs by hand; run - Prettier afterwards and let it wrap at 80 columns: - - ```bash - npx prettier --write <changed files> - ``` - -- Do not touch a line for any reason other than the finding. -- After all fixes, run `yarn build` (or `yarn start-offline` when offline) and - confirm zero broken-link errors and zero MDX compile errors. +- Change only the line the finding is about. +- Run `npx prettier --write` on changed files, then `yarn build` (or + `yarn start-offline` when offline). Zero errors before you commit. ## Output -A list of findings in the shared format: - -``` -[fix|suggestion|question] <file>:<line> — <rule> — <what is wrong> — <what was done or proposed> ``` +Files checked: N. Fixes: N. Suggestions: N. Questions: N. Suppressed: N. -Group by rule, then by file. Fixes were applied and go under "What changed" in -the PR. Suggestions go under "Suggestions (not applied)" with the proposed -text. Questions go under "Questions". Uncertain external links go under -"Questions" as a single bullet listing the URLs. - -Report counts at the top: - -``` -Files checked: N. Fixes applied: N. Suggestions: N. Questions: N. Suppressed by style-exceptions.yml: N. +[fix] docs/<path>.md:<line> — <rule> — <what was wrong> — <what you did> +[suggestion] docs/<path>.md:<line> — <rule> — <proposed change> +[question] docs/<path>.md:<line> — <rule> — <what you need to know> ``` -## When a human rejects one of your fixes +Fixes go under "What changed" in the PR, suggestions and questions under +their own headings. -That is a signal for the corrections-capture skill -(`.agents/skills/corrections-capture.md`). Do not argue in the PR. Record the -exception so it is not raised again. +If a human later rejects one of your fixes, do not argue. Hand it to +`corrections-capture.md` so it becomes an exception. diff --git a/.agents/skills/screenshot-triage.md b/.agents/skills/screenshot-triage.md index b15a70a85d67..376096b0a900 100644 --- a/.agents/skills/screenshot-triage.md +++ b/.agents/skills/screenshot-triage.md @@ -1,200 +1,74 @@ # Skill: Screenshot triage -Find screenshots that are probably out of date and rank them for a human to -retake. This skill never retakes, edits, or deletes an image. +Find screenshots that are probably stale and rank them for a human to retake. +Never retake, edit, or delete an image. ## Inputs -- A section (sidebar category or `docs/` directory), or the whole - `static/img/` tree if the user asks for a full triage. -- Read access to `OpenFn/lightning` (the web app UI) cloned to a scratch - directory. For CLI screenshots, `OpenFn/kit`. - -```bash -SCRATCH=${SCRATCH:-/tmp/openfn-src} -git clone --filter=blob:none https://github.com/OpenFn/lightning "$SCRATCH/lightning" -git clone --filter=blob:none https://github.com/OpenFn/kit "$SCRATCH/kit" -``` - -Full history is needed for dates, so do not use `--depth`. `--filter=blob:none` -keeps it fast. - -## Where images live - -All images are in `static/img/` and referenced from pages as `/img/<file>`. -Formats in use: `.webp`, `.png`, `.gif`, `.svg`, `.jpg`. Treat `.svg` files as -diagrams or logos, not screenshots, unless the alt text says otherwise. +- A section, or all of `static/img/` for a full triage. +- A clone of `OpenFn/lightning` with history (`--filter=blob:none`, not + `--depth`), and `OpenFn/kit` for CLI screenshots. ## Process -### 1. List the images in scope - -For a section, collect every image referenced by the section's pages: - -```bash -grep -h -o -E '\]\(/img/[^)]+\)|src="/img/[^"]+"' docs/<section>/*.md \ - | grep -o -E '/img/[^)"]+' | sort -u -``` - -For a full triage, list `static/img/` and also compute which images are -referenced nowhere (candidates for deletion; report, do not delete). - -### 2. Date each image - -Last commit that touched the file in this repo: - -```bash -git log -n 1 --format='%H %cs' -- static/img/<file> -``` - -Also note the referencing page, the line, the alt text, and the two lines -of prose before the image. If the image was optimised in bulk by -`scripts/optimize-images.js` (look for a commit touching many images at -once), use the commit before that bulk commit as the real date; a -re-encode is not a retake. - -### 3. Identify the UI the image depicts - -Classify each image by combining file name, alt text, surrounding prose, and -the page's section. Map it to a **UI area** and then to **source paths** in -the product repo. Use this table; extend it when you meet an unlisted area. - -| UI area | Signals in file name / alt / prose | Lightning source paths (relative to repo root) | -| -------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -| Workflow canvas | canvas, workflow diagram, nodes, edges, add step, plus icon | `assets/js/workflow-diagram/`, `lib/lightning_web/live/workflow_live/` | -| Step / job editor (Inspector) | inspector, editor, job code, adaptor picker, credential picker | `lib/lightning_web/live/workflow_live/`, `assets/js/collaborative-editor/`, `assets/js/monaco/`, `assets/js/picker/`, `assets/js/adaptor-docs/`, `assets/js/manual-run-panel/` | -| Triggers | trigger, webhook URL, cron, kafka | `lib/lightning_web/live/workflow_live/`, `lib/lightning/workflows/trigger.ex`, `lib/lightning/workflows/triggers/` | -| Runs / history / inspect run | run, history, work order, log, dataclip, output, rerun | `lib/lightning_web/live/run_live/`, `lib/lightning_web/live/dataclip_live/`, `assets/js/log-viewer/` | -| Credentials | credential, OAuth, connect, authorise | `lib/lightning_web/live/credential_live/`, `lib/lightning/credentials/` | -| Project settings | settings, collaborators, retention, GitHub sync, webhook auth, VCS | `lib/lightning_web/live/project_live/`, `lib/lightning_web/components/github_components.ex` | -| Sandboxes | sandbox, clone project, merge | `lib/lightning_web/live/sandbox_live/`, `lib/lightning_web/components/sandbox_settings_banner.ex` | -| Channels | channel, channel request | `lib/lightning_web/live/channel_live/`, `lib/lightning_web/live/channel_request_live/`, `lib/lightning/channels/` | -| Audit | audit, audit log, audit trail | `lib/lightning_web/live/audit_live/`, `lib/lightning/auditing/` | -| Dashboard / project list | dashboard, projects, metrics, overview | `lib/lightning_web/live/dashboard_live/`, `lib/lightning_web/live/project_live/` | -| User profile / tokens | profile, API token, MFA, password | `lib/lightning_web/live/profile_live/`, `lib/lightning_web/live/tokens_live/` | -| AI Assistant | assistant, chat, AI | `lib/lightning_web/live/ai_assistant/`, `lib/lightning/ai_assistant/` | -| Collections | collection, key/value | `lib/lightning_web/live/collection_live/`, `lib/lightning/collections/` | -| Login / signup / layout / navbar | login, register, sidebar, menu, navbar | `lib/lightning_web/components/layouts/`, `lib/lightning_web/live/user_live/`, `assets/css/` | -| Global styling | (applies to every screenshot) | `assets/css/app.css`, `assets/tailwind.config.ts`, `lib/lightning_web/components/core_components.ex`, `lib/lightning_web/components/layout_components.ex` | -| CLI terminal output | terminal, console, `openfn` prompt | `kit/packages/logger/src/`, `kit/packages/cli/src/<command>/` | -| Third-party UI (Kobo, DHIS2, GSheets, CommCare) | the other product's name | none in OpenFn repos; mark `external` | - -Paths change. If a listed path does not exist in the checkout, run -`git log --diff-filter=R --summary -- <path>` to follow the rename, or grep -for the LiveView module name. - -Confidence: record `high` when file name and alt text agree with the prose, -`medium` when only one of them does, `low` when you inferred from the -section alone. - -### 4. Date the UI code - -For each mapped source path set, find the newest commit that touched any of -them, excluding pure test and formatting commits: - -```bash -cd "$SCRATCH/lightning" -git log -n 1 --format='%H %cs %s' -- <path1> <path2> ... -``` - -Also record the newest commit touching the **global styling** paths, because -a theme or component-library change re-dates every screenshot. Use the later -of the two dates for each image. - -Then list the commit subjects between the image date and today for the -mapped paths, to say what likely changed: - -```bash -git log --since=<image-date> --format='%cs %s' -- <paths> | head -20 -``` - -Keep the subjects that sound user-visible (rename, redesign, move, add -button, new page, layout, colour, icon). Drop refactors, test changes, and -dependency bumps. - -### 5. Flag suspects - -An image is a **suspect** when the UI code date is later than the image -date. Compute the gap in days. - -Not suspects, but report them in a separate list: - -- `external` images (third-party UIs). Say which product and the image - date; a human decides. -- Diagrams and logos (`.svg`, or alt text says "diagram"). -- Images referenced by no page (orphans). - -### 6. Rank - -Sort suspects by: - -1. Gap size (UI code date minus image date), largest first. -2. Then by number of user-visible commits in the gap, most first. -3. Then by the page's position in the docs: anything under "Get Started" or - "Tutorials" ranks above the same gap elsewhere. - -Do not rank `low` confidence mappings above `high` ones with a similar gap; -if a low-confidence mapping would land in the top five, say so. +1. **List images in scope.** Grep the section's pages for `/img/...` + references. For a full triage, also list images referenced by no page. +2. **Date each image**: `git log -n 1 --format=%cs -- static/img/<file>`. + If the last commit was a bulk re-encode (many images, one commit), use the + commit before it. +3. **Map each image to a UI area** using the file name, alt text, and the + surrounding prose. Record confidence (high, medium, low). Then map the + area to source paths in Lightning: + + | UI area | Source paths | + | ------------------------------ | ---------------------------------------------------------------------------- | + | Canvas | `assets/js/workflow-diagram/`, `lib/lightning_web/live/workflow_live/` | + | Step editor / Inspector | `lib/lightning_web/live/workflow_live/`, `assets/js/collaborative-editor/`, `assets/js/picker/` | + | Runs, history, dataclips | `lib/lightning_web/live/run_live/`, `lib/lightning_web/live/dataclip_live/`, `assets/js/log-viewer/` | + | Credentials | `lib/lightning_web/live/credential_live/` | + | Project settings, sandboxes | `lib/lightning_web/live/project_live/`, `lib/lightning_web/live/sandbox_live/` | + | Dashboard, profile, tokens | `lib/lightning_web/live/dashboard_live/`, `profile_live/`, `tokens_live/` | + | Everything (global styling) | `assets/css/app.css`, `lib/lightning_web/components/` | + | CLI output | kit `packages/cli/src/`, `packages/logger/src/` | + | Third-party UI (Kobo, DHIS2) | none; mark `external` | + + Paths move. If one is missing, follow the rename with + `git log --diff-filter=R --summary`. +4. **Date the UI code.** Newest commit touching the mapped paths or the + global styling paths, whichever is later. List user-visible commit + subjects since the image date (renames, redesigns, new buttons); drop + refactors and dependency bumps. +5. **Flag suspects**: UI date later than image date. Report `external` + images, diagrams and logos, and orphaned images separately. +6. **Rank** by gap in days, then by number of user-visible commits, then + favour "Get Started" and "Tutorials" pages. Say if a low-confidence + mapping lands in the top five. ## Output ``` -Scope: <section or full>. Images: N. Suspects: N. External: N. Diagrams/logos: N. Orphans: N. -Lightning checked at <sha> (<date>). Kit checked at <sha>. - -Suspect screenshots (ranked): +Scope: <section>. Images: N. Suspects: N. External: N. Orphans: N. +Lightning checked at <sha>. | # | Image | Page:line | Image date | UI area (confidence) | Last UI change | Gap (days) | What likely changed | |---|-------|-----------|------------|----------------------|----------------|------------|---------------------| -| 1 | static/img/4.1_new_job.webp | docs/tutorials/kobo-to-dhis2.md:88 | 2023-02-14 | Step editor (high) | 2026-08-30 | 1293 | Inspector redesigned; adaptor picker moved to header; "Save & Run" renamed | - -External (human decides): -- static/img/2.3_kobo_rest.webp — KoboToolbox REST settings — 2023-02-14 -Diagrams and logos (skipped): ... - -Orphaned images (referenced by no page): ... +External: <list> +Orphaned: <list> ``` -Put the ranked table in the PR under "Suspect screenshots (ranked)". Cap at -the top fifteen and put the rest in a collapsed `<details>` block. - -Actions you may take in the docs repo: - -- **fix**: an image whose alt text is wrong about what the image shows (per - the mapping you just did) gets corrected alt text. -- Everything else is a report. Do not delete orphans, do not replace images, - do not edit the images. - -## Extension point: automated capture (not implemented) - -This skill is designed so that capture can plug in later without changing -the triage above. Do not implement any of this now. +Top fifteen go in the PR under "Suspect screenshots"; collapse the rest. -`OpenFn/lightning` already has Playwright end-to-end tests: config at -`assets/playwright.config.ts`, specs under `assets/test/e2e/specs/` (a -`smoke/` suite and a `collaborative/` suite at the time of writing). They -cover a subset of the UI areas above and do not yet emit docs screenshots. -When they do, or when a docs-specific capture suite is added, add a step -**7. Capture** after ranking: +The only edit you may make is correcting alt text that misdescribes the +image. Everything else is a report. -- Input: the ranked suspect list from step 6, each row carrying its - `UI area` and source paths. -- A mapping file in this repo, `screenshot-capture-map.yml` (does not exist - yet), keyed by image path, that names the Playwright test file and test - title that reaches the right screen, the selector or viewport to capture, - and any fixture data needed. Rows without a mapping stay report-only. -- The capture command runs the named test from `assets/` in the Lightning - checkout with a capture flag (for example - `npx playwright test test/e2e/specs/<file> -g "<title>"` with a custom - reporter or a `DOCS_SCREENSHOTS=1` env var the spec checks) against a - seeded local Lightning instance, writes the new image to - `static/img/<same file name>` in this repo, and records the Lightning - commit it was captured at. -- Use the test's own selectors for the capture region so the image tracks - the UI. Do not hard-code pixel crops. -- Replacement images go into the PR alongside the ranked table, with a - before/after pair in the description, and stay a **suggestion** until a - human approves the PR. +## Extension point: capture (not implemented) -Until that mapping file and those tests exist, this skill ends at step 6. +Lightning already has Playwright e2e specs under `assets/test/e2e/specs/` +(config in `assets/playwright.config.ts`). When they can emit docs +screenshots, add a step 7 that takes the ranked list, looks each image up in +a `screenshot-capture-map.yml` (image path → spec file, test title, capture +selector), runs that test with a capture flag against a seeded local +Lightning, and writes the replacement to `static/img/<same name>`. Replacement +images stay a *suggestion* with a before/after in the PR until a human +approves. Until that map and those tests exist, this skill ends at step 6. diff --git a/.agents/skills/translate.md b/.agents/skills/translate.md index d7e79764c794..48a1de855a1f 100644 --- a/.agents/skills/translate.md +++ b/.agents/skills/translate.md @@ -1,262 +1,116 @@ # Skill: Translate -Translate English docs pages into the target locales while respecting the -governance rules: glossary terms stay in English, human-reviewed pages are -never overwritten, and fenced blocks are never retranslated. - -English is the canonical source. Translations are generated artefacts that -live in the same repo and branch. - -## Target locales - -`es` (Spanish) and `fr` (French). Add a locale only when the user asks and -`docusaurus.config.js` lists it under `i18n.locales`. +Translate English docs into `es` and `fr`. English is canonical; translations +are generated artefacts committed to the same branch. ## Paths -| English source | Translation | -| ------------------------ | ------------------------------------------------------------------ | -| `docs/<a>/<b>.md` | `i18n/<locale>/docusaurus-plugin-content-docs/current/<a>/<b>.md` | -| `adaptors/<name>.md` | `i18n/<locale>/docusaurus-plugin-content-docs-adaptors/current/<name>.md` (hand-written overviews only) | -| `adaptors/packages/**` | never translated | -| `adaptors/library/**` | never translated | -| `articles/**` | not in scope unless the user asks | -| `versioned_docs/**` | never translated | - -Keep the same file name and extension as the source. +| English | Translation | +| ---------------------- | -------------------------------------------------------------------- | +| `docs/<path>.md` | `i18n/<locale>/docusaurus-plugin-content-docs/current/<path>.md` | +| `adaptors/<name>.md` | `i18n/<locale>/docusaurus-plugin-content-docs-adaptors/current/<name>.md` | +| `adaptors/packages/**`, `adaptors/library/**`, `versioned_docs/**` | never translated | ## Preconditions -Run these before translating anything. Stop with a **question** if any fails. +Stop with a *question* if any fails: -1. The English section has no open **fix** findings from lint, accuracy, or - fresh-user evaluation. Translating a page you are about to change wastes - the run. -2. `docusaurus.config.js` has an `i18n` block whose `locales` includes the - target locale. If it does not, do not add it yourself: enabling a locale - changes what `yarn build` produces and deploys. Record the question - "Enable `<locale>` in `docusaurus.config.js` i18n config?" and stop. -3. `/i18n` is not listed in `.gitignore`. If it is, stop and ask; committed - translations are the design, and the ignore rule contradicts it. -4. `glossary.yml` and `translation-rules.yml` parse as YAML. +1. The English section has no open fixes from lint, accuracy, or fresh-user + evaluation. +2. `docusaurus.config.js` lists the locale under `i18n.locales`. Do not add it + yourself; that changes what the site builds and deploys. +3. `/i18n` is not in `.gitignore`. +4. `glossary.yml` and `translation-rules.yml` parse. -## Front matter fields +## Front matter -Every translated page carries the source page's own front matter (translated -`title` and `sidebar_label`; untouched `id`, `slug`, `keywords`) plus: +Keep the source page's fields (translate `title` and `sidebar_label`; leave +`id`, `slug`, `keywords` alone) and add: ```yaml -translation_source_hash: <full git commit SHA of the last commit that touched the English page> -translation_review_status: machine # machine | human-reviewed | needs-review -translation_reviewer: # GitHub handle, only when human-reviewed -translation_review_date: # YYYY-MM-DD, only when human-reviewed -translation_model: <model identifier you are running as, e.g. the configured model id> -``` - -Get the source hash with: - -```bash -git log -n 1 --format=%H -- docs/<path>.md -``` - -Use the commit that last touched the file, not `HEAD`, so a repo-wide commit -does not invalidate every translation. - -Set `translation_model` to the exact model identifier of the session (not a -marketing name). If you cannot determine it, write `unknown` and flag it. - -## Inline override fences - -Humans mark translated content that must survive regeneration: - -```markdown -<!-- do-not-retranslate --> -Este párrafo fue corregido por un revisor humano. -<!-- /do-not-retranslate --> +translation_source_hash: <git log -n 1 --format=%H -- docs/<path>.md> +translation_review_status: machine # machine | human-reviewed | needs-review +translation_reviewer: # only when human-reviewed +translation_review_date: # only when human-reviewed +translation_model: <your model identifier> ``` -Rules: - -- Everything between the opening and closing marker, including the markers, - is copied byte for byte into the regenerated page at the same position - relative to the surrounding structure (same heading, same paragraph - index). -- If the English content that the fenced block corresponds to was deleted, - keep the fenced block and add a **question** to the PR: "Fenced block at - L<line> has no English counterpart any more; delete or keep?" -- If a fence is unclosed, treat everything to end of file as fenced and add - a **fix** to the PR closing the fence; do not regenerate the page until a - human confirms. -- Nested fences are invalid. Stop and ask. - -## Process, per page and per locale - -### 1. Load rules - -Read `glossary.yml`. Build the set of terms with `translate: false` and the -`patterns` list. Read `translation-rules.yml` and keep the rules whose -`locale` is the target or `*`. - -### 2. Decide the action - -```bash -SRC=docs/<path>.md -DST=i18n/<locale>/docusaurus-plugin-content-docs/current/<path>.md -SRC_HASH=$(git log -n 1 --format=%H -- "$SRC") -``` - -| Translation exists? | `translation_review_status` | `translation_source_hash` == `$SRC_HASH`? | Action | -| ------------------- | --------------------------- | ----------------------------------------- | ------------------------------- | -| no | | | **A. Full translation** | -| yes | `machine` or `needs-review` | any | **B. Regenerate with fences** | -| yes | `human-reviewed` | yes | **Skip.** Current and approved. | -| yes | `human-reviewed` | no | **C. Suggested diff PR** | -| yes | missing or invalid | | Treat as `needs-review`; add a **question** noting the missing field | - -### 3A. Full translation - -1. Split the source into segments: front matter, headings, paragraphs, - lists, tables, admonitions, code blocks, HTML/JSX blocks, images, links. -2. Translate prose segments. Rules: - - Glossary terms and pattern matches stay verbatim, including their - capitalisation. When a glossary entry has `product_noun: true`, keep - the word untranslated only where it names the OpenFn concept; translate - ordinary-English uses ("run the command" may be translated; "a Run" may - not). - - Apply every matching rule in `translation-rules.yml`. - - Default register: Spanish "tú", French "vous", unless a rule says - otherwise. - - Translate `title`, `sidebar_label`, admonition titles (`:::tip Título`), - table headers, image alt text, and link text. - - Do not translate `id`, `slug`, `keywords`, heading anchors set with - `{#anchor}`, HTML attribute names, or anything inside backticks. - - Preserve Markdown and MDX structure exactly: same heading levels, same - list markers, same admonition types, same `<details>`/`<Tabs>` - components with the same props. -3. Copy code blocks (fenced and inline) byte for byte. Translate only - comments inside fenced blocks when the block's language is `js`, `bash`, - `yaml`, or `json` with `//` or `#` comments and the comment is prose, not - a command. Leave string literals, keys, and identifiers alone. -4. Rewrite internal links: - - `/documentation/...`, `/adaptors/...` and `/articles/...` become - `/<locale>/documentation/...` etc. Docusaurus resolves them at build - time, but explicit locale prefixes keep translated pages linking to - translated pages. Exception: links into `adaptors/packages/...` stay - unprefixed, because those pages are English-only. - - Relative `.md` links stay relative (they resolve inside the locale - tree). - - `#fragment` anchors: Docusaurus slugifies the translated heading, so a - translated heading changes the anchor. Either add an explicit - `{#original-anchor}` to the translated heading (preferred; keeps - English anchors stable across locales) or update the fragment. Do the - former. -5. Write front matter: source fields plus the five translation fields with - `translation_review_status: machine`. -6. Write the file at `$DST`, creating directories as needed. - -### 3B. Regenerate with fences - -1. Read the existing translation. Extract every - `<!-- do-not-retranslate -->` block with its position (the nearest - preceding heading and the paragraph index under it). -2. Perform 3A on the current English source. -3. Re-insert each fenced block at the matching position. If the position no - longer exists, append it under the nearest surviving heading and record a - **question**. -4. Keep `translation_review_status` as it was if it was `needs-review`; - otherwise set `machine`. Update `translation_source_hash` and - `translation_model`. - -### 3C. Suggested diff for a human-reviewed page - -Never write to `$DST`. - -1. Compute the English change since the recorded hash: - - ```bash - OLD=$(grep -m1 translation_source_hash "$DST" | awk '{print $2}') - git diff "$OLD" "$SRC_HASH" -- "$SRC" - ``` - -2. Translate only the changed or added English hunks, following 3A rules. -3. Produce a unified diff against the current `$DST` that applies those - translated hunks at the corresponding positions and updates - `translation_source_hash` to `$SRC_HASH`. Leave - `translation_review_status: human-reviewed`, `translation_reviewer`, and - `translation_review_date` untouched in the diff; the reviewer decides - whether to keep the status. -4. Put the diff in a **separate PR** titled - `translate(<locale>): suggested update for <path> (human-reviewed)`, - request review from `translation_reviewer`, and reference the English - commit range in the body. If several human-reviewed pages in the same - section need updates, one PR for all of them is fine. Do not mix these - diffs into the main section PR. +Use the last commit that touched the file, not `HEAD`. + +## Fences + +Content between `<!-- do-not-retranslate -->` and +`<!-- /do-not-retranslate -->` is copied byte for byte into the regenerated +page at the same position. If its English counterpart was deleted, keep the +block and raise a *question*. Unclosed or nested fences: stop and ask. + +## Decide the action + +| Translation exists? | Status | Hash matches current English? | Action | +| ------------------- | --------------------------- | ----------------------------- | ------------------- | +| no | | | Full translation | +| yes | `machine` or `needs-review` | any | Regenerate, keep fences | +| yes | `human-reviewed` | yes | Skip | +| yes | `human-reviewed` | no | Suggested diff | +| yes | missing | | Treat as `needs-review`, raise a *question* | + +## Translating + +1. Load `glossary.yml` (terms with `translate: false` and `patterns` stay + verbatim) and the matching locale's rules from `translation-rules.yml`. + For `product_noun` terms, keep the word untranslated only where it names + the OpenFn concept. +2. Translate prose, headings, admonition titles, table headers, alt text, and + link text. Default register: Spanish "tú", French "vous". +3. Copy code blocks and inline code byte for byte. Translate only prose + comments inside fenced blocks. +4. Keep Markdown and MDX structure identical: heading levels, list markers, + admonition types, components and props. +5. Prefix internal links with `/<locale>` except links into + `adaptors/packages/`, which are English-only. Add `{#original-anchor}` to + translated headings so English fragment links keep working. +6. Regenerating: extract the fences first, translate the current English, + then reinsert the fences. Keep `needs-review` status if it was set; + otherwise `machine`. + +## Suggested diff for human-reviewed pages + +Never write to the file. Diff the English between the recorded hash and now, +translate only the changed hunks, and produce a patch against the current +translation that applies them and updates `translation_source_hash`. Leave +the review status fields alone. Open a separate PR titled +`translate(<locale>): suggested update for <path>` and request the +`translation_reviewer`. ## Quality checks -Run on every page you wrote (3A and 3B) before committing. Any failure is a -**fix** you make now. - -1. **Glossary**: every `translate: false` term that appears in the English - prose appears the same number of times, verbatim, in the translated - prose. Product nouns with `product_noun: true` may appear fewer times - only if the English used the word in its ordinary sense. - - ```bash - for t in OpenFn Lightning adaptor workflow; do - printf '%s: %s -> %s\n' "$t" "$(grep -o -i -w "$t" "$SRC" | wc -l)" "$(grep -o -i -w "$t" "$DST" | wc -l)" - done - ``` - -2. **Code blocks**: extract all fenced blocks from source and translation; - after stripping comment lines they must be identical, in the same order. - - ```bash - diff <(awk '/^```/{f=!f; print; next} f && !/^\s*(\/\/|#)/' "$SRC") \ - <(awk '/^```/{f=!f; print; next} f && !/^\s*(\/\/|#)/' "$DST") - ``` - -3. **Structure**: same count of headings per level, same count of fenced - blocks, admonitions, images, and tables. - -4. **Links**: every internal link in the translation resolves. Run the - locale build: - - ```bash - yarn docusaurus build --locale <locale> - ``` - - `onBrokenLinks: 'throw'` makes this authoritative. - -5. **Front matter**: all required translation fields present, hash is 40 hex - characters, status is one of the three allowed values, model is set. +Any failure is a fix you make before committing: -6. **Fences**: every `<!-- do-not-retranslate -->` in the old file is - present in the new one, byte for byte. +- Every `translate: false` glossary term appears as often in the translation + as in the source. +- Fenced code blocks, minus comment lines, are identical and in the same + order. +- Same count of headings per level, code blocks, admonitions, images, tables. +- `yarn docusaurus build --locale <locale>` passes (broken links throw). +- All translation front matter fields present; hash is 40 hex characters. +- Every fence from the old file is present in the new one. ## Output ``` -Locale: es -Pages: N. Full: N. Regenerated: N. Skipped (human-reviewed, current): N. Suggested-diff PRs: N. +Locale: es. Pages: N. Full: N. Regenerated: N. Skipped: N. Suggested-diff PRs: N. -| Source | Action | Source hash | Fences kept | Checks | -| ------ | ------ | ----------- | ----------- | ------ | -| docs/jobs/state.md | regenerate | a1b2c3d | 2 | pass | +| Source | Action | Hash | Fences kept | Checks | +|--------|--------|------|-------------|--------| -Questions: -[question] i18n/es/.../state.md L40 — fenced block has no English counterpart since <hash> — keep or delete? +[question] ... ``` -Commit translations per locale: `translate(es): <section>`. Count each -translated file toward the 20-file limit in `AGENTS.md`. +Commit per locale: `translate(es): <section>`. Each translated file counts +toward the 20-file limit. ## Do not -- Do not translate generated adaptor reference pages, ever. -- Do not translate `versioned_docs/`. -- Do not "improve" the English source while translating. Record the issue as - a finding for the next English pass. -- Do not change `translation_review_status` to `human-reviewed`. Only a - human sets that, by hand, with their handle and the date. -- Do not commit a translation whose quality checks fail. +- Improve the English while translating. Record it for the next English pass. +- Set `human-reviewed`. Only a human does that. +- Commit a translation that fails a quality check. diff --git a/AGENTS.md b/AGENTS.md index d7354132eca4..3ed690a64ef8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,230 +1,121 @@ # AGENTS.md: docs maintenance agent for OpenFn/docs -You are the documentation maintenance agent for the OpenFn docs site -(https://docs.openfn.org). This repo is a Docusaurus 3 project. Your job is to -keep one section of the docs at a time accurate, readable, complete, lint-clean, -and (once the English is clean) translated. +You maintain the OpenFn docs site (https://docs.openfn.org), a Docusaurus 3 +project. You work on one section at a time and keep it accurate, readable, +complete, lint-clean, and, once the English is clean, translated. -Read this file first. Then load only the skill files you need from -`.agents/skills/`. Each skill file is self-contained. +Read this file first. Load skills from `.agents/skills/` as you need them; +each is self-contained. ## Repo map -| Path | What it is | Editable? | -| ------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------- | -| `docs/**/*.md`, `docs/**/*.mdx` | Hand-written English docs (canonical source) | Yes | -| `sidebars-main.js` | Navigation for `docs/` | Yes | -| `adaptors/*.md`, `adaptors/intro.mdx` | Hand-written adaptor overview pages | Yes | -| `adaptors/packages/**` | Generated adaptor reference (functions, config schema, changelog, readme) from JSDoc | **No.** Fix at source in `OpenFn/adaptors` | -| `adaptors/library/jobs/auto/**` | Generated job library | **No.** Generated at build | -| `sidebars-adaptors.js` | Adaptor navigation (mostly derived from generated `publicPaths.json`) | Only the hand-written parts | -| `articles/` | Blog-style help articles | Yes, only when a section named includes it | -| `versioned_docs/version-legacy/**` | Frozen v1 docs (banner: unmaintained) | No. Record findings as suggestions only | -| `static/img/**` | Images and screenshots | Yes (metadata only; never retake images) | -| `i18n/{locale}/docusaurus-plugin-content-docs/current/` | Translations (generated artefacts, same repo, same branch) | Yes, via the translate skill rules | -| `glossary.yml` | Product terms that are never translated, plus spelling variants lint should flag | Yes, via corrections-capture | -| `style-exceptions.yml` | Lint findings humans have rejected; do not flag again | Yes, via corrections-capture | -| `translation-rules.yml` | Locale-specific phrasing rules learned from human edits | Yes, via corrections-capture | -| `docusaurus.config.js` | Site config | Ask before editing | - -Product source repos (read-only, for verification): - -- `OpenFn/lightning`: the web app (Platform). Elixir/Phoenix. UI lives under - `lib/lightning_web/` and `assets/`. -- `OpenFn/kit`: the CLI (`@openfn/cli`), runtime, compiler, and deploy - tooling. Note that `@openfn/language-common` lives in `OpenFn/adaptors`, - not here. -- `OpenFn/adaptors`: the adaptor monorepo. JSDoc in `packages/<name>/src/` is - the source of every page under `adaptors/packages/`. - -The generated adaptor pages are not in git. `yarn generate-adaptors` fetches -`docs.json` from the `docs` branch of `OpenFn/adaptors` and writes the pages at -build time. Any inaccuracy you find there is a JSDoc bug in -`OpenFn/adaptors`, not a docs-repo bug. - -## Skills - -| Skill | File | When | -| ---------------------- | ------------------------------------------- | ------------------------------------------------------------- | -| Lint | `.agents/skills/lint.md` | Always first. Deterministic style and structure checks. | -| Accuracy check | `.agents/skills/accuracy-check.md` | After lint. Verify every claim against product code. | -| Fresh-user evaluation | `.agents/skills/fresh-user-eval.md` | After accuracy. Read cold, try to do the task. | -| Gap analysis | `.agents/skills/gap-analysis.md` | After fresh-user eval. What is missing from this section? | -| Translate | `.agents/skills/translate.md` | Last, and only once the English section has no open fixes. | -| Corrections capture | `.agents/skills/corrections-capture.md` | Whenever a human has overridden a previous agent output. | -| Screenshot triage | `.agents/skills/screenshot-triage.md` | On request, or when a section contains images. | - -## Default execution order - -1. Confirm the section (see "Scope" below). -2. Run **lint**. Apply fixes. Record suggestions. -3. Run **accuracy check** on every page in the section. Apply fixes. Record - suggestions and questions. Draft `OpenFn/adaptors` issues for generated-page - problems. -4. Run **fresh-user evaluation** on every page in the section. Apply fixes. - Record scores, suggestions, and questions. -5. Run **gap analysis** for the section. Record the ranked gap list. Do not - write new pages unless the user asked for them. -6. If the section contains images, run **screenshot triage** and record the - ranked list. Never retake screenshots. -7. Only if steps 2 to 4 left zero open fixes and zero unanswered questions for - the section: run **translate** for each target locale (`es`, `fr`). -8. Open the PR (see "Stopping and the PR"). - -If the user names a single skill, run only that skill on the named section and -still finish with a PR. - -## Scope: one section at a time - -A "section" is one top-level or nested category in `sidebars-main.js` (for -example "Get Started", "Write Jobs", "Platform > Monitor History", "CLI"), or -one directory under `docs/`, or a single page if the user names one. - -- Never process the whole site in one run. -- If the user has not named a section, stop and ask which one. List the - categories from `sidebars-main.js` so they can pick. -- Stay inside the section. If a finding requires a change outside it (a - broken link target, a glossary term), make that single change and note it - under "Also touched" in the PR. +| Path | What it is | Editable? | +| ------------------------------------- | -------------------------------------------- | ------------------------------------------ | +| `docs/**` | English docs, the canonical source | Yes | +| `sidebars-main.js` | Navigation for `docs/` | Yes | +| `adaptors/*.md` | Hand-written adaptor overviews | Yes | +| `adaptors/packages/**`, `adaptors/library/**` | Generated at build time from JSDoc in `OpenFn/adaptors`. Not in git. | **No.** Fix the JSDoc upstream | +| `versioned_docs/**` | Frozen v1 docs | No. Suggestions only | +| `static/img/**` | Images | Alt text only. Never retake images | +| `i18n/<locale>/**` | Translations (generated artefacts) | Yes, via `translate.md` rules | +| `glossary.yml`, `style-exceptions.yml`, `translation-rules.yml` | Rules the skills read | Yes, via `corrections-capture.md` | +| `docusaurus.config.js`, `package.json`, `.github/` | Build and deploy | Ask first | -## Classifying findings - -Every finding from every skill gets exactly one class: - -- **fix**: Objectively wrong or mechanically checkable, and the correct value - is known from the code, the build, or a config file. Apply it directly. - Examples: typo in a CLI flag, a dead link, a heading that skips a level, a - code block missing a language tag, an incorrect default value verified in - source. -- **suggestion**: A judgement call about wording, structure, emphasis, or - scope where a reasonable author could disagree. Do not apply. Record it in - the PR description with the proposed text so a human can accept it. -- **question**: The docs and the code disagree and you cannot tell which is - intended, or the page implies a product behaviour you cannot verify, or the - right fix depends on a decision you do not own. Do not guess. Record it in - the PR description as a question with what you checked and what the - candidates are. If the question blocks the rest of the section, stop and - ask the user. - -When in doubt between fix and suggestion, choose suggestion. When in doubt -between suggestion and question, choose question. - -## Hard rules - -1. **Never edit a page whose front matter has - `translation_review_status: human-reviewed`.** Produce the change as a - suggested diff in the PR description (or a separate PR if the diff is - large) for the named `translation_reviewer` to approve. -2. **Never edit generated adaptor reference pages** (`adaptors/packages/**`, - `adaptors/library/jobs/auto/**`). Write an issue for `OpenFn/adaptors` - naming the package, the JSDoc block, and the correction. Put the draft - issue body in the PR description under "Upstream issues". Only file the - issue if the user has asked you to file issues. -3. **Never retranslate content inside `<!-- do-not-retranslate -->` fences.** -4. **Never translate glossary terms.** Load `glossary.yml` before touching any - translation. -5. **Never edit `versioned_docs/`.** Legacy v1 docs are frozen. -6. **Never change `docusaurus.config.js`, `package.json`, or CI workflows** - without asking first. These affect the production build. -7. **Never retake, crop, or regenerate screenshots.** Triage only. -8. **Never commit secrets, personal data, or internal URLs** you find in - product repos. -9. **Never use skipped-test, disabled-check, or "ignore" workarounds** to get - the build green. If `yarn build` fails after your changes, fix the cause. - -## Stopping and the PR - -Stop and open a PR when either happens first: - -- The section is done (every skill in the order above has run or been - explicitly skipped), or -- You have changed **20 files**. Count every created, modified, or deleted - file, including translations and YAML config files. When you reach 20, stop - the current skill cleanly, do not start another, and open the PR. Say in the - PR which pages in the section were not reached. - -Before opening the PR: +Product code, read-only, for verification: `OpenFn/lightning` (web app), +`OpenFn/kit` (CLI, runtime, compiler), `OpenFn/adaptors` (adaptors and +`language-common`). Clone them into a scratch directory, not this repo. -1. Run `yarn build` (or `yarn start-offline` if network is unavailable, then - confirm the changed pages render). `onBrokenLinks` is set to `throw`, so a - broken internal link fails the build. -2. Run Prettier on changed markdown: `npx prettier --write <files>`. The repo - uses `.prettierrc` with `proseWrap: always` and `printWidth: 80`. -3. Re-read your diff. Remove anything that is not a **fix**. +## Skills and order -Work on a branch named `docs-agent/<section-slug>` unless the user gave you a -branch. Commit in small, labelled commits (`lint: ...`, `accuracy: ...`, -`fresh-user: ...`, `translate(es): ...`). +1. `.agents/skills/lint.md`: deterministic style and structure checks. +2. `.agents/skills/accuracy-check.md`: verify every claim against code. +3. `.agents/skills/fresh-user-eval.md`: read cold, try to do the task. +4. `.agents/skills/gap-analysis.md`: what is missing from this section. +5. `.agents/skills/screenshot-triage.md`: if the section has images. +6. `.agents/skills/translate.md`: only when steps 1 to 3 left no open fixes + or questions. -The PR description follows `.github/pull_request_template.md`. Tick "I have used -Claude Code" under AI Usage. Then add these sections: +`.agents/skills/corrections-capture.md` runs whenever a human has overridden a +previous agent output. -```markdown -## Section +If the user names a single skill, run only that one and still finish with a +PR. -<sidebar category or directory>, <N> pages. Skills run: <list>. +## Scope -## What changed +A section is one category in `sidebars-main.js`, one directory under `docs/`, +or one page if the user names one. Never process the whole site in a run. If +no section is named, stop and ask, listing the categories. -- <page>: <one line per fix, grouped by skill> +Stay inside the section. If a fix requires touching a file outside it, make +that one change and list it under "Also touched" in the PR. -## Suggestions (not applied) - -- <page> L<line>: <current text> → <proposed text>. Reason: <one sentence>. - -## Questions - -- <page>: <what the docs say> vs <what the code says at repo/path:line>. Which is intended? - -## Skipped +## Classifying findings -- <page>: human-reviewed translation, suggested diff below -- <page>: generated, see Upstream issues -- <pages not reached because the 20-file limit was hit> +- **Fix**: objectively wrong and the correct value is known from code, the + build, or config. Apply it. Keep it local; never rewrite voice or structure + under the banner of a fix. +- **Suggestion**: a judgement call a reasonable author could disagree with. + Record it in the PR with the proposed text. Do not apply. +- **Question**: docs and code disagree and you cannot tell which is intended, + or the answer depends on a decision you do not own. Do not guess. Ask, and + stop if it blocks the rest of the section. -## Upstream issues (OpenFn/adaptors) +When in doubt, downgrade: fix → suggestion → question. -<draft issue bodies, one per package> +## Hard rules -## Scores (fresh-user evaluation) +1. Never edit a page with `translation_review_status: human-reviewed`. + Produce a suggested diff instead. +2. Never edit generated adaptor pages. Draft an issue for `OpenFn/adaptors` + and put it in the PR under "Upstream issues". File it only if asked. +3. Never retranslate content inside `<!-- do-not-retranslate -->` fences. +4. Never translate glossary terms. +5. Never edit `versioned_docs/`. +6. Never change build or deploy config without asking. +7. Never retake, crop, or regenerate screenshots. +8. Never commit secrets or personal data found in product repos. +9. Never disable or skip a check to get the build green. -| Page | Readability | Completeness | -| ---- | ----------- | ------------ | +## Stopping and the PR -## Gaps (ranked) +Stop and open a PR when the section is done or when you have changed +**20 files**, whichever comes first. Every created, modified, or deleted file +counts. Say which pages were not reached. -<ranked list from gap analysis> +Before opening the PR: run `npx prettier --write` on changed markdown, run +`yarn build` (broken links fail the build), and re-read your diff, removing +anything that is not a fix. -## Suspect screenshots (ranked) +Branch: `docs-agent/<section-slug>` unless told otherwise. Commit per skill +(`lint: ...`, `accuracy: ...`, `translate(es): ...`). -<ranked list from screenshot triage> -``` +Follow `.github/pull_request_template.md`, tick "I have used Claude Code", +and add these sections, omitting any that are empty: -Omit any section that is empty. +- **Section**: name, page count, skills run. +- **What changed**: one line per fix, grouped by skill. +- **Suggestions**: page, line, current text, proposed text, reason. +- **Questions**: what the docs say, what the code says, what you need to know. +- **Skipped**: human-reviewed pages, generated pages, pages not reached. +- **Upstream issues**: draft bodies for `OpenFn/adaptors`. +- **Scores**: readability and completeness per page. +- **Gaps** and **Suspect screenshots**: ranked lists. -## Shared finding format +## Finding format -Every skill records findings in this shape so they can be merged into the PR: +Every skill records findings as: ``` -[fix|suggestion|question] <file path>:<line> — <what is wrong> — <what to do> +[fix|suggestion|question] <file>:<line> — <what is wrong> — <what to do> ``` -Line numbers refer to the file as it was before your edits. - -## Conventions you must respect while editing - -- Front matter is YAML between `---` fences. Pages in `docs/` use `title`, - optionally `sidebar_label`, `id`, `slug`, `keywords`. Do not invent new - fields except the translation fields defined in - `.agents/skills/translate.md`. -- Internal links use absolute site paths: `/documentation/<path>` for `docs/`, - `/adaptors/<path>` for adaptors, `/articles/<path>` for articles. -- Docusaurus admonitions (`:::tip`, `:::note`, `:::warning`, `:::info`, - `:::caution`) must have a blank line before and after the fences. -- Images live in `static/img/` and are referenced as `/img/<file>`. Every image - needs alt text. -- The spelling is **adaptor**, never "adapter". Terminology is defined in - `docs/get-started/terminology.md` and pinned in `glossary.yml`. -- Do not rewrite a page's voice or structure under the banner of a fix. Fixes - are local. +## Conventions + +- Front matter is YAML with `title`, optionally `sidebar_label`, `id`, + `slug`, `keywords`. Add only the translation fields from `translate.md`. +- Internal links are site-absolute: `/documentation/...`, `/adaptors/...`, + `/articles/...`. +- Images live in `static/img/` and are referenced as `/img/<file>`. +- The spelling is **adaptor**, never "adapter". Terms are pinned in + `glossary.yml` and explained in `docs/get-started/terminology.md`. From 0b5f0b1d0ed91f3e1c97ea3b6e191c9fd85f1ee4 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 11:25:32 +0000 Subject: [PATCH 03/13] Cut skills and orchestrator in half again Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/accuracy-check.md | 98 +++++------------- .agents/skills/corrections-capture.md | 80 +++++---------- .agents/skills/fresh-user-eval.md | 109 ++++++-------------- .agents/skills/gap-analysis.md | 106 +++++-------------- .agents/skills/lint.md | 100 +++++------------- .agents/skills/screenshot-triage.md | 104 ++++++------------- .agents/skills/translate.md | 138 ++++++++----------------- AGENTS.md | 142 ++++++++------------------ 8 files changed, 251 insertions(+), 626 deletions(-) diff --git a/.agents/skills/accuracy-check.md b/.agents/skills/accuracy-check.md index 6af77bf33527..088468dee169 100644 --- a/.agents/skills/accuracy-check.md +++ b/.agents/skills/accuracy-check.md @@ -1,84 +1,38 @@ # Skill: Accuracy check -Extract every verifiable claim from a docs page and check it against the code -that implements it. +Verify every claim on a page against the code that implements it. -## Inputs - -- The page(s) to check. -- Read-only clones of the product repos in a scratch directory (never inside - this repo): - - | Claim is about | Repo | Start in | - | --------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------- | - | Web app UI, triggers, credentials, runs, projects, API | `OpenFn/lightning` | `lib/lightning_web/router.ex`, `lib/lightning_web/live/`, `config/runtime.exs` | - | CLI commands and flags, `openfn deploy`, `project.yaml` | `OpenFn/kit` | `packages/cli/src/`, `packages/deploy/`, `packages/project/` | - | Job syntax, `state`, `fn()`, `each()`, `$` operator | `OpenFn/kit` and `OpenFn/adaptors` | `packages/compiler/`, `packages/runtime/`; `packages/common/src/` in adaptors | - | Adaptor functions, config schema, versions | `OpenFn/adaptors` | `packages/<name>/src/Adaptor.js`, `configuration-schema.json`, `package.json` | - -Use the default branch unless the page names a version. Say which commit you -checked in the output. +Where to look: web app, UI, API → `OpenFn/lightning` (`router.ex`, +`lib/lightning_web/live/`, `config/runtime.exs`). CLI, deploy, `project.yaml`, +job syntax → `OpenFn/kit` (`packages/cli`, `packages/deploy`, +`packages/compiler`). Adaptor functions and config → `OpenFn/adaptors` +(`packages/<name>/src/Adaptor.js`, `configuration-schema.json`). Use the +default branch unless the page names a version; record the commit. ## Process -1. **List claims.** Read the page once and number every statement a reader - could act on and be wrong about: code samples, function or endpoint - signatures, UI labels and menu paths, config options and defaults, version - numbers, file paths, limits and timeouts. Skip motivation, analogies, and - v1 history callouts. -2. **Check each claim** and record `repo/path:line` as evidence. - - Code samples: compile job code with `openfn compile <file> -a <adaptor>`; - run shell commands where they are read-only (`--help`). Never run anything - that deploys, deletes, sends, or touches a live OpenFn instance. - - Names and labels: grep for the exact string. If missing, check - `git log -S '<name>'` for a rename. - - Config: find the constant or `System.get_env` / `process.env` read and - compare the default. - - Versions: compare against `package.json` or `mix.exs` and the latest tag. -3. **Classify** each claim as pass, fail, or uncertain. Uncertain covers - deployment-specific values (limits, retention), anything that needs a live - system, and product policy or pricing (not verifiable from code; the owner - is the product team). -4. **Decide the action** for each failure: - - *Fix* when the correct value is in the code and slots into the existing - sentence (a flag, default, label, version, path). - - *Suggestion* when the fix needs a rewritten paragraph or the docs may - describe intended behaviour. - - *Question* when docs and code disagree and either could be the bug. Say - which you suspect. +1. Number every actionable claim: code samples, signatures, UI labels, config + options and defaults, versions, paths, limits. Skip motivation and v1 + history callouts. +2. Check each with evidence (`repo/path:line`). Compile job samples with + `openfn compile`. Run only read-only commands; never touch a live instance. + If a name is missing, check `git log -S` for a rename. +3. Mark pass, fail, or uncertain. Uncertain includes deployment-specific + values, anything needing a live system, and policy or pricing (owner: the + product team). +4. For failures: *fix* when the right value slots into the sentence; + *suggestion* when a paragraph must change; *question* when either side + could be the bug. ## Generated adaptor pages -Pages under `adaptors/packages/` are rendered from JSDoc in `OpenFn/adaptors`. -Never edit them here. For each failure, find the JSDoc block in -`packages/<name>/src/` and draft an issue: - -```markdown -## <name>: docs for `<function>()` do not match behaviour - -Page: https://docs.openfn.org/adaptors/packages/<name>-docs#<anchor> -Source: packages/<name>/src/Adaptor.js L<line> -Docs say: <quote> -Code does: <one sentence, with line reference> -Suggested JSDoc: <corrected block> -``` - -Put drafts under "Upstream issues" in the PR. File them only if the user asked -you to. If a hand-written overview `adaptors/<name>.md` repeats the same -error, that copy is a normal fix. +Never edit `adaptors/packages/**`. Find the JSDoc in `OpenFn/adaptors` and +draft an issue: page URL, source file and line, what docs say, what code does, +suggested JSDoc. Put it in the PR under "Upstream issues"; file only if asked. +If `adaptors/<name>.md` repeats the error, that is a normal fix. ## Output -``` -Page: docs/<path>.md — checked against lightning@<sha>, kit@<sha>, adaptors@<sha> -Claims: N. Pass: N. Fail: N. Uncertain: N. - -| # | Claim | Result | Evidence | -|---|-------|--------|----------| - -[fix] docs/<path>.md:<line> — docs say X, code does Y (repo/path:line) — changed to Z -[suggestion] ... -[question] ... -``` - -Apply fixes, run Prettier, confirm `yarn build` passes. +Per page: commits checked; pass/fail/uncertain counts; a table of claims with +evidence; then findings in the shared format. Apply fixes, run Prettier and +`yarn build`. diff --git a/.agents/skills/corrections-capture.md b/.agents/skills/corrections-capture.md index 3e144cf48c8e..ffddbb56e04d 100644 --- a/.agents/skills/corrections-capture.md +++ b/.agents/skills/corrections-capture.md @@ -1,56 +1,28 @@ # Skill: Corrections capture -When a human overrides agent output, turn the override into a rule so the same -correction is never needed twice. Capture the pattern, not the one-off. - -## Triggers - -| A human... | Rule file | -| --------------------------------------------------- | -------------------------------------------- | -| edits a machine-translated page | `glossary.yml` or `translation-rules.yml` | -| marks a translation `human-reviewed` | `translation-rules.yml` (mine the diff) | -| reverts or rejects a lint fix or suggestion | `style-exceptions.yml` | -| rewrites a section the fresh-user eval flagged | `style-exceptions.yml`, if the pattern should not be flagged again | -| reverts an accuracy fix | No rule. Raise a *question* for the product team. | - -Find candidates with `git log --no-merges -- i18n/` filtered to human -authors, `git log --grep=revert -i`, and review comments on the agent's PRs. - -## Process - -1. **Pair** the agent's change with the human's change - (`git diff <agent-commit> <human-commit> -- <file>`). Discard pairs where - the human change is unrelated. -2. **Extract the rule.** Ask in order: - - Terminology? A word or phrase replaced in a way that applies everywhere. - English page: add or correct a `glossary.yml` entry. Translated page - where an English term was restored: add it with `translate: false`. - Translated page where a non-product phrase was re-rendered: a - `translation-rules.yml` entry with `kind: term`. - - Translation pattern? Register, punctuation, UI label handling, a - rendering to avoid: `translation-rules.yml` with the matching `kind`. - Write the `instruction` so it applies without seeing the example. - - Rejected lint finding? `style-exceptions.yml`, scoped as narrowly as the - evidence supports: one page unless the reviewer said otherwise. - - Factual disagreement? No rule. Record a *question*. - - If you cannot state the rule so that it applies to at least one other - page, skip it. -3. **Write the rule** following the file's header schema. Fill `reason` - (quote the review comment if there is one), `added_by` (the human's - GitHub handle), `added_on`, and `source_pr`. No other personal data. -4. **Check for conflicts.** No duplicate glossary terms. No exception broad - enough to disable a rule repo-wide. No contradictory translation rules - for the same locale and phrase; keep the newer and flag the conflict. -5. **Apply retroactively where cheap.** New glossary variant: fix it across - `docs/`. New translation rule: leave other pages for the next translate - run. -6. **Open a PR** titled `rules: capture corrections from <PR or commit>` - listing what was overridden, the rules added, and any questions. Rules - take effect only after merge. - -## Do not - -- Capture typos or one-sentence rewordings as rules. -- Edit the human's change. -- Set `human-reviewed` on anything. +When a human overrides agent output, record the general rule, not the fix. + +| A human... | Add to | +| ------------------------------------------- | ----------------------------------------- | +| edits a machine translation | `glossary.yml` (restored English term) or `translation-rules.yml` (phrasing, register, punctuation, UI labels) | +| rejects or reverts a lint fix or suggestion | `style-exceptions.yml`, scoped to one page unless told otherwise | +| rewrites a section fresh-user eval flagged | `style-exceptions.yml` if the pattern should not be flagged again | +| reverts an accuracy fix | nothing; raise a *question* for the product team | + +Find candidates via human commits to `i18n/`, `git log --grep=revert -i`, and +review comments on agent PRs. + +1. Diff the agent's change against the human's. Drop unrelated pairs. +2. State the rule so it applies to at least one other page. If you cannot, + it is a one-off: skip it. +3. Write it per the file's header schema, with `reason` (quote the review + comment if any), `added_by` (their GitHub handle), `added_on`, `source_pr`. +4. Check conflicts: no duplicate glossary terms, no repo-wide exception that + disables a rule, no contradicting translation rules (keep the newer, flag + it). +5. Apply cheap retroactive fixes (new glossary variants across `docs/`). + Leave other translations for the next translate run. +6. Open a PR `rules: capture corrections from <source>` listing overrides, + rules added, and questions. Rules apply only after merge. + +Never edit the human's change. Never set `human-reviewed`. diff --git a/.agents/skills/fresh-user-eval.md b/.agents/skills/fresh-user-eval.md index 7422d82c0fec..84ff3503bc17 100644 --- a/.agents/skills/fresh-user-eval.md +++ b/.agents/skills/fresh-user-eval.md @@ -1,81 +1,32 @@ # Skill: Fresh-user evaluation -Read a page as a new user would and try to do what it says. Report every place -you had to guess. Run this after the accuracy check so you are evaluating a -page whose facts are already right. - -## Inputs - -- One page at a time. -- Read access to the product repos, only for pass 3. - -## Process - -**Pass 1: read cold.** Assume you know what webhooks, APIs, JSON, and a -terminal are, and nothing about OpenFn. Read the page once without following -links. Write down in one sentence what it teaches and who it is for. If you -cannot, that is your first finding. - -**Pass 2: do the task.** Follow the page literally. - -- Procedural pages: perform each step. For web app steps, confirm the named - button or page exists in `OpenFn/lightning` (`lib/lightning_web/live/`). - For CLI steps, run the commands. -- Conceptual pages: explain the concept back in two sentences, then answer - three questions a new user would ask, using only the page. -- Reference pages: pick three entries and check you could use each from its - description alone. - -Record a finding each time you: had to guess a term or path the page never -defined; got stuck because a step depends on something the page did not say; -hit a sentence with two readings; needed a prerequisite the page assumes; -found steps in an order that does not work; could not tell what success looks -like. - -**Pass 3: verify your guesses.** Check the code or neighbouring pages. A -wrong guess is strong evidence the page needs the information. A right guess -is still a finding. - -## Classify - -- *Fix*: a single verified fact that fits in one sentence at a specific line. -- *Suggestion*: a new subsection, example, screenshot, or rewrite of more - than a couple of sentences. Propose the text; do not apply. -- *Question*: you could not find the answer, or the fix depends on the - intended audience. - -Do not fix tone or voice. Do not add beyond what the finding needs. - -## Scores - -**Readability (1 to 5)**: 5 means understood on one read; 3 means got the -gist despite undefined terms; 1 means unintelligible without outside -knowledge. - -**Completeness (1 to 5)**: could a new user finish the task with only this -page? 5 yes, including knowing they are done; 3 yes after following links or -guessing more than once; 1 the page does not describe how to do the task. - -One sentence per score naming what cost points. - -## Output - -``` -Page: docs/<path>.md -Teaches: <one sentence>. Audience: <phrase>. -Attempted: <two sentences> - -[fix] docs/<path>.md:<line> — <what was missing> — added "<text>" -[suggestion] docs/<path>.md:<line> — <what was missing> — propose <text> -[question] docs/<path>.md:<line> — <ambiguity> — <options> - -Readability: N/5 — <why> -Completeness: N/5 — <why> -``` - -## Do not - -- Read the page's git history before pass 1. -- Evaluate generated adaptor pages. Evaluate `adaptors/<name>.md` instead. -- Edit human-reviewed translations. Suggested diffs only. -- Write a new page. A task with no page is a gap for `gap-analysis.md`. +Read one page as a new user and try to do what it says. Run after the +accuracy check. + +1. **Read cold.** Assume you know webhooks, APIs, JSON, and a terminal, and + nothing about OpenFn. Do not follow links or read git history. Write one + sentence on what the page teaches and for whom. If you cannot, that is a + finding. +2. **Do the task.** Procedural page: perform each step, confirming web app + elements exist in `OpenFn/lightning` and running CLI commands. Conceptual + page: explain it back in two sentences and answer three likely user + questions from the page alone. Reference page: use three entries from their + descriptions. Record a finding wherever you guessed an undefined term, got + stuck, hit an ambiguous sentence, needed an unstated prerequisite, found + steps out of order, or could not tell what success looks like. +3. **Verify guesses** against code or neighbouring pages. Wrong guesses are + strong findings; right guesses still count. + +Classify: *fix* for a single verified fact that fits one sentence; +*suggestion* for a new subsection, example, or rewrite; *question* when the +answer depends on audience or is unknown. Do not fix tone. + +Score, whole numbers, one sentence each: + +- **Readability 1 to 5**: 5 understood in one read; 3 gist only; 1 needs + outside knowledge. +- **Completeness 1 to 5**: 5 task done from this page alone; 3 done after + links or guesses; 1 page does not describe the task. + +Output: teaches, audience, what you attempted, findings in the shared format, +both scores. Do not evaluate generated adaptor pages or write new pages. diff --git a/.agents/skills/gap-analysis.md b/.agents/skills/gap-analysis.md index f6bc9bef7d6d..a655d2f39f4e 100644 --- a/.agents/skills/gap-analysis.md +++ b/.agents/skills/gap-analysis.md @@ -1,80 +1,30 @@ # Skill: Gap analysis -Compare what a docs section covers against what exists in the product and -what users ask about. Produce a ranked list of gaps. Do not write the missing -pages unless asked. - -## Inputs - -- A section (sidebar category or `docs/` directory). -- Read-only clones of the product repos (see the table in - `accuracy-check.md` for where things live). -- Optional: GitHub issues on `OpenFn/docs`, the community forum - (community.openfn.org), support channels, and search analytics. Use only - what the session actually has access to, and say which in the output. Never - invent user demand. - -## Process - -1. **Inventory the docs.** For each page, list what it covers using headings - and tables. Note links out of the section: those are things it assumes are - documented elsewhere. Check that they are. -2. **Inventory the product**, choosing what matches the section: - - Web app: routes in `router.ex`, LiveViews in `lib/lightning_web/live/`, - env vars in `config/runtime.exs`. - - CLI: `packages/cli/src/commands.ts` and `openfn --help`. - - Job writing: exports of `packages/common/src/` in adaptors, transforms in - `packages/compiler/` in kit. - - Deployment: `DEPLOYMENT.md`, `docker-compose.yml`, `config/runtime.exs`. - - Adaptors: hand-written overviews (`adaptors/<name>.md`) missing for - heavily used adaptors. -3. **Diff.** For each product item with no matching docs item, search the - whole `docs/`, `articles/`, and `adaptors/*.md` trees before calling it a - gap. Label each gap: - - **missing page**: nothing in the docs mentions it. - - **partial page**: the right page exists but does not cover this item. - - **misplaced**: documented, but not where a user on this task would look. - - **stale**: documented for an older version. Hand to `accuracy-check.md`. - - Also note docs items that no longer exist in the product. -4. **Check user signals** if available: issues, forum threads, or support - questions matching the section's terms. Count them; do not quote people. -5. **Rank** by scoring each gap 1 to 5 on reach (how many users hit it), - severity (what goes wrong without it), evidence (5 with repeated user asks, - 3 if prominent in the UI or CLI, 1 if only found in code), and effort - (5 if a paragraph fixes it, 1 if it needs a tutorial). Sum and sort. - -## Output - -``` -Section: <name>, N pages. Product checked: lightning@<sha>, kit@<sha>. -Sources used: <list>. - -1. [missing page] <title> — 17/20 (reach 5, severity 4, evidence 4, effort 4) - Missing: <two sentences> - Evidence: <repo/path:line>; <issue or thread count> - Should live: docs/<dir>/<slug>.md, sidebar "<Category>" after "<page>" - Outline: <H2 list> - -2. [partial page] docs/<path>.md lacks <item> — 14/20 (...) - Should live: new "## <heading>" after "## <existing heading>" - Outline: ... - -Docs items no longer in the product: <list, handed to accuracy check> -``` - -Put the top ten in the PR under "Gaps (ranked)"; collapse the rest in a -`<details>` block. - -## Actions - -- *Fix*: add a one-sentence cross-link when the target page clearly exists. -- Everything else is a *suggestion*. Create pages only if the user asked, and - then add them to `sidebars-main.js` and run `yarn build`. - -## Do not - -- Count thin generated adaptor pages as gaps here; they are JSDoc issues for - `OpenFn/adaptors`. -- Propose documenting feature-flagged or experimental behaviour. Raise a - *question* for the product team instead. +Compare what a section covers with what exists in the product and what users +ask. Rank the gaps. Write pages only if asked. + +1. **Inventory the docs**: what each page covers, from headings and tables. + Check that links out of the section point at pages that exist. +2. **Inventory the product** for the matching area: routes and LiveViews in + `OpenFn/lightning`; `packages/cli/src/commands.ts` and `openfn --help` in + `OpenFn/kit`; `packages/common/src/` exports in `OpenFn/adaptors`; + `DEPLOYMENT.md` and `config/runtime.exs` for deployment. +3. **Diff.** Search all of `docs/`, `articles/`, and `adaptors/*.md` before + calling something a gap. Label each: **missing page**, **partial page** + (right page exists, item absent), **misplaced**, or **stale** (hand to + `accuracy-check.md`). Note docs items the product no longer has. +4. **User signals**, only if accessible: `OpenFn/docs` issues, the community + forum, support channels, search analytics. Count, do not quote. Never + invent demand. +5. **Rank**: score 1 to 5 on reach, severity, evidence (5 repeated asks, 3 + prominent in UI, 1 code only), and effort (5 a paragraph, 1 a tutorial). + Sum and sort. + +Output per gap: label, score, what is missing, evidence, where it should live +(file, sidebar position or heading), suggested outline. Top ten in the PR; +collapse the rest. + +Only *fix*: a one-sentence cross-link to a page that clearly exists. +Everything else is a *suggestion*. Thin generated adaptor pages are JSDoc +issues, not gaps. Feature-flagged behaviour is a *question* for the product +team. diff --git a/.agents/skills/lint.md b/.agents/skills/lint.md index 9290b736737e..48bb168c9641 100644 --- a/.agents/skills/lint.md +++ b/.agents/skills/lint.md @@ -1,77 +1,27 @@ # Skill: Lint -Deterministic style and structure checks on docs markdown. Run this first on -any section. Most findings are fixes you apply directly. - -## Inputs - -- A section: a sidebar category in `sidebars-main.js`, a directory under - `docs/`, or a single page. -- `glossary.yml` (terminology) and `style-exceptions.yml` (findings humans - have rejected). Load both before you start. Drop any finding that matches an - exception. - -## Never lint-fix - -- `adaptors/packages/**` and `adaptors/library/**`: generated from - `OpenFn/adaptors` at build time. Record as an upstream issue. -- `versioned_docs/**`: frozen v1 docs. Suggestions only. -- Pages with `translation_review_status: human-reviewed`. Suggested diff only. - -## Checks - -Prose only: skip code blocks, inline code, URLs, and front matter when -matching text. - -1. **Terminology.** Replace every `variants` spelling from `glossary.yml` - with its `term`, keeping capitalisation and plurals. Flag case variants - when `case_sensitive` is true. Skip ordinary-English uses of - `product_noun` terms. Always fix "adapter" → "adaptor". *Fix.* -2. **Heading hierarchy.** No H1 in the body (the title comes from front - matter). No skipped levels. No duplicate heading text in one page. *Fix.* - Mixed heading case within a page is a *suggestion*, because headings are - anchors; only convert if you also update every inbound `#fragment` link. -3. **Internal links.** Every `/documentation/...`, `/adaptors/...`, - `/articles/...`, relative `.md`, and `#fragment` link must resolve. The - build is authoritative: `onBrokenLinks` is `throw`, so run `yarn build`. - *Fix* when the intended target is unambiguous, otherwise *question*. -4. **External links.** Check each `http(s)` link with `curl -IL`. 404 or 410 - after two tries is dead: *fix* if there is an obvious successor, else - *suggestion*. 403 and 429 are not dead; mark uncertain. Upgrade `http://` - to `https://` where it works. -5. **Orphaned pages.** A page in `docs/` that appears in no sidebar and is - linked from no other page. *Suggestion* (add to sidebar or delete); never - auto-fix, someone may be drafting it. -6. **Front matter.** Every page needs valid YAML with at least `title`. - Translated pages also need the fields listed in `translate.md`. Missing - `title`: *fix* from the body H1. Unparseable YAML: *fix* by quoting. -7. **Code block language.** Every fence needs a tag. Use `js`, `json`, - `yaml`, `bash`, or `text`. *Fix.* -8. **Image alt text.** Every image needs alt text that says what it shows, - not "image" or the file name. Write it from the surrounding paragraph. - Missing image file: *fix* if exactly one match exists in `static/img/`, - else *question*. -9. **Admonitions.** `:::tip` and friends need a blank line inside both - fences. *Fix.* - -## Applying fixes - -- Change only the line the finding is about. -- Run `npx prettier --write` on changed files, then `yarn build` (or - `yarn start-offline` when offline). Zero errors before you commit. - -## Output - -``` -Files checked: N. Fixes: N. Suggestions: N. Questions: N. Suppressed: N. - -[fix] docs/<path>.md:<line> — <rule> — <what was wrong> — <what you did> -[suggestion] docs/<path>.md:<line> — <rule> — <proposed change> -[question] docs/<path>.md:<line> — <rule> — <what you need to know> -``` - -Fixes go under "What changed" in the PR, suggestions and questions under -their own headings. - -If a human later rejects one of your fixes, do not argue. Hand it to -`corrections-capture.md` so it becomes an exception. +Deterministic checks on a docs section. Run first. Load `glossary.yml` and +`style-exceptions.yml`; skip findings matching an exception. Never touch +generated adaptor pages, `versioned_docs/`, or human-reviewed translations. + +Check prose only (not code, URLs, or front matter): + +1. **Terminology**: replace glossary `variants` with `term`; always fix + "adapter" → "adaptor". *Fix.* +2. **Headings**: no body H1, no skipped levels, no duplicates. *Fix.* Mixed + case is a *suggestion* (headings are anchors). +3. **Internal links**: must resolve. `yarn build` is authoritative + (`onBrokenLinks: throw`). *Fix* if the target is obvious, else *question*. +4. **External links**: 404/410 after two tries is dead. *Fix* if there is a + clear successor, else *suggestion*. 403/429 are uncertain, not dead. +5. **Orphans**: pages in no sidebar and linked from nowhere. *Suggestion.* +6. **Front matter**: valid YAML with `title`. *Fix* from the body H1. +7. **Code fences**: need a language (`js`, `json`, `yaml`, `bash`, `text`). *Fix.* +8. **Alt text**: must describe the image, not "screenshot". *Fix* from context. +9. **Admonitions**: blank line inside both `:::` fences. *Fix.* + +Change only the offending line. Run Prettier, then `yarn build`, before +committing. + +Output: counts, then one line per finding in the shared format. If a human +rejects a fix, hand it to `corrections-capture.md`. diff --git a/.agents/skills/screenshot-triage.md b/.agents/skills/screenshot-triage.md index 376096b0a900..64c0e5d90732 100644 --- a/.agents/skills/screenshot-triage.md +++ b/.agents/skills/screenshot-triage.md @@ -1,74 +1,34 @@ # Skill: Screenshot triage -Find screenshots that are probably stale and rank them for a human to retake. -Never retake, edit, or delete an image. - -## Inputs - -- A section, or all of `static/img/` for a full triage. -- A clone of `OpenFn/lightning` with history (`--filter=blob:none`, not - `--depth`), and `OpenFn/kit` for CLI screenshots. - -## Process - -1. **List images in scope.** Grep the section's pages for `/img/...` - references. For a full triage, also list images referenced by no page. -2. **Date each image**: `git log -n 1 --format=%cs -- static/img/<file>`. - If the last commit was a bulk re-encode (many images, one commit), use the - commit before it. -3. **Map each image to a UI area** using the file name, alt text, and the - surrounding prose. Record confidence (high, medium, low). Then map the - area to source paths in Lightning: - - | UI area | Source paths | - | ------------------------------ | ---------------------------------------------------------------------------- | - | Canvas | `assets/js/workflow-diagram/`, `lib/lightning_web/live/workflow_live/` | - | Step editor / Inspector | `lib/lightning_web/live/workflow_live/`, `assets/js/collaborative-editor/`, `assets/js/picker/` | - | Runs, history, dataclips | `lib/lightning_web/live/run_live/`, `lib/lightning_web/live/dataclip_live/`, `assets/js/log-viewer/` | - | Credentials | `lib/lightning_web/live/credential_live/` | - | Project settings, sandboxes | `lib/lightning_web/live/project_live/`, `lib/lightning_web/live/sandbox_live/` | - | Dashboard, profile, tokens | `lib/lightning_web/live/dashboard_live/`, `profile_live/`, `tokens_live/` | - | Everything (global styling) | `assets/css/app.css`, `lib/lightning_web/components/` | - | CLI output | kit `packages/cli/src/`, `packages/logger/src/` | - | Third-party UI (Kobo, DHIS2) | none; mark `external` | - - Paths move. If one is missing, follow the rename with - `git log --diff-filter=R --summary`. -4. **Date the UI code.** Newest commit touching the mapped paths or the - global styling paths, whichever is later. List user-visible commit - subjects since the image date (renames, redesigns, new buttons); drop - refactors and dependency bumps. -5. **Flag suspects**: UI date later than image date. Report `external` - images, diagrams and logos, and orphaned images separately. -6. **Rank** by gap in days, then by number of user-visible commits, then - favour "Get Started" and "Tutorials" pages. Say if a low-confidence - mapping lands in the top five. - -## Output - -``` -Scope: <section>. Images: N. Suspects: N. External: N. Orphans: N. -Lightning checked at <sha>. - -| # | Image | Page:line | Image date | UI area (confidence) | Last UI change | Gap (days) | What likely changed | -|---|-------|-----------|------------|----------------------|----------------|------------|---------------------| - -External: <list> -Orphaned: <list> -``` - -Top fifteen go in the PR under "Suspect screenshots"; collapse the rest. - -The only edit you may make is correcting alt text that misdescribes the -image. Everything else is a report. - -## Extension point: capture (not implemented) - -Lightning already has Playwright e2e specs under `assets/test/e2e/specs/` -(config in `assets/playwright.config.ts`). When they can emit docs -screenshots, add a step 7 that takes the ranked list, looks each image up in -a `screenshot-capture-map.yml` (image path → spec file, test title, capture -selector), runs that test with a capture flag against a seeded local -Lightning, and writes the replacement to `static/img/<same name>`. Replacement -images stay a *suggestion* with a before/after in the PR until a human -approves. Until that map and those tests exist, this skill ends at step 6. +Rank screenshots likely to be stale. Never retake, edit, or delete images. +Needs `OpenFn/lightning` cloned with history (`--filter=blob:none`). + +1. List `/img/...` references in the section (full triage: all of + `static/img/`, noting images no page uses). +2. Date each image with `git log -n 1 --format=%cs -- static/img/<file>`, + skipping bulk re-encode commits. +3. Map each image to a UI area from file name, alt text, and prose, with + confidence high/medium/low, then to Lightning paths: canvas + `assets/js/workflow-diagram/`; step editor `lib/lightning_web/live/ + workflow_live/`, `assets/js/collaborative-editor/`; runs and history + `run_live/`, `dataclip_live/`, `assets/js/log-viewer/`; credentials + `credential_live/`; project settings `project_live/`, `sandbox_live/`; + global styling `assets/css/app.css`, `lib/lightning_web/components/`. CLI + output maps to kit `packages/cli/src/`. Third-party UIs are `external`. +4. Date the UI: newest commit touching the mapped or global styling paths. + List user-visible commit subjects since the image date. +5. Suspect = UI newer than image. Rank by gap in days, then user-visible + commits, then Get Started and Tutorials first. Flag low-confidence + mappings in the top five. + +Output a table: image, page:line, image date, UI area (confidence), last UI +change, gap, what likely changed. List external, orphaned, and diagram images +separately. Top fifteen in the PR. The only edit allowed is correcting wrong +alt text. + +**Extension point, not implemented**: Lightning has Playwright specs in +`assets/test/e2e/specs/`. When they can emit docs screenshots, add step 6: +look each suspect up in a `screenshot-capture-map.yml` (image → spec, test +title, selector), run it with a capture flag against a seeded local +Lightning, write to `static/img/<same name>`, and present before/after as a +*suggestion*. diff --git a/.agents/skills/translate.md b/.agents/skills/translate.md index 48a1de855a1f..890a212000c5 100644 --- a/.agents/skills/translate.md +++ b/.agents/skills/translate.md @@ -1,116 +1,60 @@ # Skill: Translate -Translate English docs into `es` and `fr`. English is canonical; translations -are generated artefacts committed to the same branch. +Translate `docs/**` and `adaptors/*.md` into `es` and `fr`. English is +canonical. Output goes to +`i18n/<locale>/docusaurus-plugin-content-docs/current/<same path>` (adaptor +overviews: `...-content-docs-adaptors/current/`). Never translate +`adaptors/packages/**`, `adaptors/library/**`, or `versioned_docs/**`. -## Paths +## Preconditions (stop with a *question* if any fails) -| English | Translation | -| ---------------------- | -------------------------------------------------------------------- | -| `docs/<path>.md` | `i18n/<locale>/docusaurus-plugin-content-docs/current/<path>.md` | -| `adaptors/<name>.md` | `i18n/<locale>/docusaurus-plugin-content-docs-adaptors/current/<name>.md` | -| `adaptors/packages/**`, `adaptors/library/**`, `versioned_docs/**` | never translated | - -## Preconditions - -Stop with a *question* if any fails: - -1. The English section has no open fixes from lint, accuracy, or fresh-user - evaluation. -2. `docusaurus.config.js` lists the locale under `i18n.locales`. Do not add it - yourself; that changes what the site builds and deploys. -3. `/i18n` is not in `.gitignore`. -4. `glossary.yml` and `translation-rules.yml` parse. +English section has no open fixes. `docusaurus.config.js` lists the locale +under `i18n.locales` (do not add it yourself). `/i18n` is not gitignored. +`glossary.yml` and `translation-rules.yml` parse. ## Front matter -Keep the source page's fields (translate `title` and `sidebar_label`; leave -`id`, `slug`, `keywords` alone) and add: +Keep the source fields (translate `title` and `sidebar_label` only) and add: ```yaml -translation_source_hash: <git log -n 1 --format=%H -- docs/<path>.md> +translation_source_hash: <git log -n 1 --format=%H -- <english file>> translation_review_status: machine # machine | human-reviewed | needs-review -translation_reviewer: # only when human-reviewed -translation_review_date: # only when human-reviewed -translation_model: <your model identifier> +translation_reviewer: # human-reviewed only +translation_review_date: # human-reviewed only +translation_model: <your model id> ``` -Use the last commit that touched the file, not `HEAD`. - -## Fences - -Content between `<!-- do-not-retranslate -->` and -`<!-- /do-not-retranslate -->` is copied byte for byte into the regenerated -page at the same position. If its English counterpart was deleted, keep the -block and raise a *question*. Unclosed or nested fences: stop and ask. - -## Decide the action - -| Translation exists? | Status | Hash matches current English? | Action | -| ------------------- | --------------------------- | ----------------------------- | ------------------- | -| no | | | Full translation | -| yes | `machine` or `needs-review` | any | Regenerate, keep fences | -| yes | `human-reviewed` | yes | Skip | -| yes | `human-reviewed` | no | Suggested diff | -| yes | missing | | Treat as `needs-review`, raise a *question* | - -## Translating +## Action -1. Load `glossary.yml` (terms with `translate: false` and `patterns` stay - verbatim) and the matching locale's rules from `translation-rules.yml`. - For `product_noun` terms, keep the word untranslated only where it names - the OpenFn concept. -2. Translate prose, headings, admonition titles, table headers, alt text, and - link text. Default register: Spanish "tú", French "vous". -3. Copy code blocks and inline code byte for byte. Translate only prose - comments inside fenced blocks. -4. Keep Markdown and MDX structure identical: heading levels, list markers, - admonition types, components and props. -5. Prefix internal links with `/<locale>` except links into - `adaptors/packages/`, which are English-only. Add `{#original-anchor}` to - translated headings so English fragment links keep working. -6. Regenerating: extract the fences first, translate the current English, - then reinsert the fences. Keep `needs-review` status if it was set; - otherwise `machine`. +| Exists? | Status | Hash current? | Action | +| ------- | ----------------------- | ------------- | ---------------------------------------- | +| no | | | Full translation | +| yes | machine / needs-review | any | Regenerate, preserving fences | +| yes | human-reviewed | yes | Skip | +| yes | human-reviewed | no | Translate only the changed English hunks; open a separate PR with the diff for `translation_reviewer`. Never write the file. | -## Suggested diff for human-reviewed pages +Fences: content between `<!-- do-not-retranslate -->` and +`<!-- /do-not-retranslate -->` is copied byte for byte at the same position. +If its English source is gone, keep it and raise a *question*. -Never write to the file. Diff the English between the recorded hash and now, -translate only the changed hunks, and produce a patch against the current -translation that applies them and updates `translation_source_hash`. Leave -the review status fields alone. Open a separate PR titled -`translate(<locale>): suggested update for <path>` and request the -`translation_reviewer`. +## Rules -## Quality checks - -Any failure is a fix you make before committing: - -- Every `translate: false` glossary term appears as often in the translation - as in the source. -- Fenced code blocks, minus comment lines, are identical and in the same - order. -- Same count of headings per level, code blocks, admonitions, images, tables. -- `yarn docusaurus build --locale <locale>` passes (broken links throw). -- All translation front matter fields present; hash is 40 hex characters. -- Every fence from the old file is present in the new one. - -## Output - -``` -Locale: es. Pages: N. Full: N. Regenerated: N. Skipped: N. Suggested-diff PRs: N. - -| Source | Action | Hash | Fences kept | Checks | -|--------|--------|------|-------------|--------| - -[question] ... -``` +- Glossary terms with `translate: false` and `patterns` stay verbatim. + `product_noun` terms stay only where they name the OpenFn concept. +- Apply the locale's `translation-rules.yml` rules. Default: Spanish "tú", + French "vous". +- Code blocks and inline code are copied byte for byte (prose comments may be + translated). Markdown and MDX structure stays identical. +- Prefix internal links with `/<locale>`, except links into + `adaptors/packages/`. Add `{#original-anchor}` to translated headings. -Commit per locale: `translate(es): <section>`. Each translated file counts -toward the 20-file limit. +## Checks before committing -## Do not +Glossary terms appear as often as in the source. Code blocks identical. Same +counts of headings, fences, admonitions, images, tables. All front matter +fields present. Every old fence preserved. `yarn docusaurus build --locale +<locale>` passes. -- Improve the English while translating. Record it for the next English pass. -- Set `human-reviewed`. Only a human does that. -- Commit a translation that fails a quality check. +Commit per locale (`translate(es): <section>`). Each file counts toward the +20-file limit. Never set `human-reviewed`; never improve the English while +translating. diff --git a/AGENTS.md b/AGENTS.md index 3ed690a64ef8..8c0dda4eab2d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,121 +1,65 @@ # AGENTS.md: docs maintenance agent for OpenFn/docs -You maintain the OpenFn docs site (https://docs.openfn.org), a Docusaurus 3 -project. You work on one section at a time and keep it accurate, readable, -complete, lint-clean, and, once the English is clean, translated. - -Read this file first. Load skills from `.agents/skills/` as you need them; -each is self-contained. +You maintain the OpenFn docs (Docusaurus 3), one section at a time. Skills +live in `.agents/skills/`; each is self-contained. ## Repo map -| Path | What it is | Editable? | -| ------------------------------------- | -------------------------------------------- | ------------------------------------------ | -| `docs/**` | English docs, the canonical source | Yes | -| `sidebars-main.js` | Navigation for `docs/` | Yes | -| `adaptors/*.md` | Hand-written adaptor overviews | Yes | -| `adaptors/packages/**`, `adaptors/library/**` | Generated at build time from JSDoc in `OpenFn/adaptors`. Not in git. | **No.** Fix the JSDoc upstream | -| `versioned_docs/**` | Frozen v1 docs | No. Suggestions only | -| `static/img/**` | Images | Alt text only. Never retake images | -| `i18n/<locale>/**` | Translations (generated artefacts) | Yes, via `translate.md` rules | -| `glossary.yml`, `style-exceptions.yml`, `translation-rules.yml` | Rules the skills read | Yes, via `corrections-capture.md` | -| `docusaurus.config.js`, `package.json`, `.github/` | Build and deploy | Ask first | - -Product code, read-only, for verification: `OpenFn/lightning` (web app), -`OpenFn/kit` (CLI, runtime, compiler), `OpenFn/adaptors` (adaptors and -`language-common`). Clone them into a scratch directory, not this repo. - -## Skills and order - -1. `.agents/skills/lint.md`: deterministic style and structure checks. -2. `.agents/skills/accuracy-check.md`: verify every claim against code. -3. `.agents/skills/fresh-user-eval.md`: read cold, try to do the task. -4. `.agents/skills/gap-analysis.md`: what is missing from this section. -5. `.agents/skills/screenshot-triage.md`: if the section has images. -6. `.agents/skills/translate.md`: only when steps 1 to 3 left no open fixes - or questions. - -`.agents/skills/corrections-capture.md` runs whenever a human has overridden a -previous agent output. - -If the user names a single skill, run only that one and still finish with a -PR. - -## Scope - -A section is one category in `sidebars-main.js`, one directory under `docs/`, -or one page if the user names one. Never process the whole site in a run. If -no section is named, stop and ask, listing the categories. - -Stay inside the section. If a fix requires touching a file outside it, make -that one change and list it under "Also touched" in the PR. +- `docs/**`, `sidebars-main.js`, `adaptors/*.md`: editable English source. +- `adaptors/packages/**`, `adaptors/library/**`: generated at build time from + JSDoc in `OpenFn/adaptors`. Never edit; fix upstream. +- `versioned_docs/**`: frozen v1 docs. Never edit. +- `i18n/<locale>/**`: translations, governed by `translate.md`. +- `glossary.yml`, `style-exceptions.yml`, `translation-rules.yml`: rules. +- `docusaurus.config.js`, `package.json`, `.github/`: ask before editing. -## Classifying findings +Product code for verification, read-only, cloned outside this repo: +`OpenFn/lightning` (web app), `OpenFn/kit` (CLI, runtime), `OpenFn/adaptors`. -- **Fix**: objectively wrong and the correct value is known from code, the - build, or config. Apply it. Keep it local; never rewrite voice or structure - under the banner of a fix. -- **Suggestion**: a judgement call a reasonable author could disagree with. - Record it in the PR with the proposed text. Do not apply. -- **Question**: docs and code disagree and you cannot tell which is intended, - or the answer depends on a decision you do not own. Do not guess. Ask, and - stop if it blocks the rest of the section. +## Order -When in doubt, downgrade: fix → suggestion → question. +lint → accuracy-check → fresh-user-eval → gap-analysis → screenshot-triage +(if images) → translate (only when the English has no open fixes or +questions). Run `corrections-capture` whenever a human has overridden agent +output. If the user names one skill, run only that. -## Hard rules +## Scope -1. Never edit a page with `translation_review_status: human-reviewed`. - Produce a suggested diff instead. -2. Never edit generated adaptor pages. Draft an issue for `OpenFn/adaptors` - and put it in the PR under "Upstream issues". File it only if asked. -3. Never retranslate content inside `<!-- do-not-retranslate -->` fences. -4. Never translate glossary terms. -5. Never edit `versioned_docs/`. -6. Never change build or deploy config without asking. -7. Never retake, crop, or regenerate screenshots. -8. Never commit secrets or personal data found in product repos. -9. Never disable or skip a check to get the build green. +A section is one `sidebars-main.js` category, one `docs/` directory, or one +page. Never the whole site. No section named: ask, listing the categories. -## Stopping and the PR +## Findings -Stop and open a PR when the section is done or when you have changed -**20 files**, whichever comes first. Every created, modified, or deleted file -counts. Say which pages were not reached. +- **fix**: objectively wrong, correct value known from code or build. Apply, + locally. Never rewrite voice or structure as a "fix". +- **suggestion**: a judgement call. Record in the PR with proposed text. +- **question**: docs and code disagree, or the decision is not yours. Ask. -Before opening the PR: run `npx prettier --write` on changed markdown, run -`yarn build` (broken links fail the build), and re-read your diff, removing -anything that is not a fix. +When in doubt, downgrade. -Branch: `docs-agent/<section-slug>` unless told otherwise. Commit per skill -(`lint: ...`, `accuracy: ...`, `translate(es): ...`). +Format: `[fix|suggestion|question] <file>:<line> — <problem> — <action>` -Follow `.github/pull_request_template.md`, tick "I have used Claude Code", -and add these sections, omitting any that are empty: +## Hard rules -- **Section**: name, page count, skills run. -- **What changed**: one line per fix, grouped by skill. -- **Suggestions**: page, line, current text, proposed text, reason. -- **Questions**: what the docs say, what the code says, what you need to know. -- **Skipped**: human-reviewed pages, generated pages, pages not reached. -- **Upstream issues**: draft bodies for `OpenFn/adaptors`. -- **Scores**: readability and completeness per page. -- **Gaps** and **Suspect screenshots**: ranked lists. +Never edit a page with `translation_review_status: human-reviewed` (suggest a +diff). Never edit generated adaptor pages (draft an `OpenFn/adaptors` issue; +file only if asked). Never retranslate inside `<!-- do-not-retranslate -->` +fences. Never translate glossary terms. Never retake screenshots. Never change +build config without asking. Never disable a check to get green. -## Finding format +## Stopping and the PR -Every skill records findings as: +Stop when the section is done or after **20 changed files**, whichever comes +first. Before the PR: `npx prettier --write` on changed files, `yarn build` +(broken links fail it), re-read the diff. Branch `docs-agent/<section>`. -``` -[fix|suggestion|question] <file>:<line> — <what is wrong> — <what to do> -``` +Use `.github/pull_request_template.md`, tick "I have used Claude Code", and +add: what changed, suggestions, questions, skipped (human-reviewed, generated, +not reached), upstream issues, scores, gaps, suspect screenshots. Omit empty +sections. ## Conventions -- Front matter is YAML with `title`, optionally `sidebar_label`, `id`, - `slug`, `keywords`. Add only the translation fields from `translate.md`. -- Internal links are site-absolute: `/documentation/...`, `/adaptors/...`, - `/articles/...`. -- Images live in `static/img/` and are referenced as `/img/<file>`. -- The spelling is **adaptor**, never "adapter". Terms are pinned in - `glossary.yml` and explained in `docs/get-started/terminology.md`. +Front matter needs `title`. Internal links are site-absolute +(`/documentation/...`). Images are `/img/<file>` in `static/img/`. Spelling is +**adaptor**, never "adapter". Terms are in `glossary.yml`. From cbc654216df11f6b373b95a94b37787c201d083d Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 11:29:06 +0000 Subject: [PATCH 04/13] Rewrite agent skills for human readability Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/accuracy-check.md | 80 +++++++++------ .agents/skills/corrections-capture.md | 80 +++++++++------ .agents/skills/fresh-user-eval.md | 91 +++++++++++------ .agents/skills/gap-analysis.md | 82 +++++++++------ .agents/skills/lint.md | 80 ++++++++++----- .agents/skills/screenshot-triage.md | 90 ++++++++++------- .agents/skills/translate.md | 113 +++++++++++++-------- AGENTS.md | 138 +++++++++++++++++--------- 8 files changed, 481 insertions(+), 273 deletions(-) diff --git a/.agents/skills/accuracy-check.md b/.agents/skills/accuracy-check.md index 088468dee169..7d1ae90913ea 100644 --- a/.agents/skills/accuracy-check.md +++ b/.agents/skills/accuracy-check.md @@ -1,38 +1,52 @@ -# Skill: Accuracy check - -Verify every claim on a page against the code that implements it. - -Where to look: web app, UI, API → `OpenFn/lightning` (`router.ex`, -`lib/lightning_web/live/`, `config/runtime.exs`). CLI, deploy, `project.yaml`, -job syntax → `OpenFn/kit` (`packages/cli`, `packages/deploy`, -`packages/compiler`). Adaptor functions and config → `OpenFn/adaptors` -(`packages/<name>/src/Adaptor.js`, `configuration-schema.json`). Use the -default branch unless the page names a version; record the commit. - -## Process - -1. Number every actionable claim: code samples, signatures, UI labels, config - options and defaults, versions, paths, limits. Skip motivation and v1 - history callouts. -2. Check each with evidence (`repo/path:line`). Compile job samples with - `openfn compile`. Run only read-only commands; never touch a live instance. - If a name is missing, check `git log -S` for a rename. -3. Mark pass, fail, or uncertain. Uncertain includes deployment-specific - values, anything needing a live system, and policy or pricing (owner: the - product team). -4. For failures: *fix* when the right value slots into the sentence; - *suggestion* when a paragraph must change; *question* when either side - could be the bug. +# Accuracy check + +Go through a page and check that everything it claims is actually true in the +code. + +## Where to look + +- Anything about the web app, its screens, settings, or API: `OpenFn/lightning`. +- Anything about the CLI, deploying, `project.yaml`, or how job code is + compiled: `OpenFn/kit`. +- Anything about a specific adaptor's functions or credentials: + `OpenFn/adaptors`, under `packages/<adaptor name>/`. + +Use the main branch unless the page names a version. Write down which commit +you checked against. + +## Steps + +1. **List the claims.** Read the page and number every statement a reader + could act on: code samples, function names and arguments, button and menu + names, settings and their defaults, version numbers, file paths, limits. + Skip the motivational text and the "in v1 this used to be" notes. +2. **Check each one.** Find the code that backs it up and note the file and + line. For job code samples, run `openfn compile` to make sure they at least + compile. Only run commands that read; never deploy, delete, or hit a live + OpenFn instance. If a name is missing from the code, look at the git + history in case it was renamed. +3. **Mark each claim** pass, fail, or uncertain. Uncertain covers things you + cannot verify from code: values set per deployment, anything that needs a + running system, pricing, and policy. Those belong to the product team. +4. **Decide what to do about failures.** Fix it if the right value drops + straight into the sentence (a flag name, a default, a label). Suggest it if + the paragraph would need rewriting. Ask if you cannot tell whether the docs + or the code is the one that is wrong, and say which you suspect. ## Generated adaptor pages -Never edit `adaptors/packages/**`. Find the JSDoc in `OpenFn/adaptors` and -draft an issue: page URL, source file and line, what docs say, what code does, -suggested JSDoc. Put it in the PR under "Upstream issues"; file only if asked. -If `adaptors/<name>.md` repeats the error, that is a normal fix. +The pages under `adaptors/packages/` are built from code comments in +`OpenFn/adaptors`. Do not edit them here. Instead, find the comment in +`packages/<name>/src/` and write up an issue: the page URL, the source file +and line, what the docs say, what the code does, and the corrected comment. +Put it in the PR under "Upstream issues". Only file it if the user asked you +to. -## Output +If a hand-written overview page (`adaptors/<name>.md`) repeats the same +mistake, fix that one normally. -Per page: commits checked; pass/fail/uncertain counts; a table of claims with -evidence; then findings in the shared format. Apply fixes, run Prettier and -`yarn build`. +## What to report + +For each page: the commits you checked, how many claims passed, failed, or +were uncertain, a table of the claims with their evidence, and the findings +in the standard format. Apply your fixes, run Prettier, and run `yarn build`. diff --git a/.agents/skills/corrections-capture.md b/.agents/skills/corrections-capture.md index ffddbb56e04d..bfade7bca140 100644 --- a/.agents/skills/corrections-capture.md +++ b/.agents/skills/corrections-capture.md @@ -1,28 +1,52 @@ -# Skill: Corrections capture - -When a human overrides agent output, record the general rule, not the fix. - -| A human... | Add to | -| ------------------------------------------- | ----------------------------------------- | -| edits a machine translation | `glossary.yml` (restored English term) or `translation-rules.yml` (phrasing, register, punctuation, UI labels) | -| rejects or reverts a lint fix or suggestion | `style-exceptions.yml`, scoped to one page unless told otherwise | -| rewrites a section fresh-user eval flagged | `style-exceptions.yml` if the pattern should not be flagged again | -| reverts an accuracy fix | nothing; raise a *question* for the product team | - -Find candidates via human commits to `i18n/`, `git log --grep=revert -i`, and -review comments on agent PRs. - -1. Diff the agent's change against the human's. Drop unrelated pairs. -2. State the rule so it applies to at least one other page. If you cannot, - it is a one-off: skip it. -3. Write it per the file's header schema, with `reason` (quote the review - comment if any), `added_by` (their GitHub handle), `added_on`, `source_pr`. -4. Check conflicts: no duplicate glossary terms, no repo-wide exception that - disables a rule, no contradicting translation rules (keep the newer, flag - it). -5. Apply cheap retroactive fixes (new glossary variants across `docs/`). - Leave other translations for the next translate run. -6. Open a PR `rules: capture corrections from <source>` listing overrides, - rules added, and questions. Rules apply only after merge. - -Never edit the human's change. Never set `human-reviewed`. +# Corrections capture + +When a human changes something the agent did, do not treat it as a one-off. +Work out the general rule behind it and write that rule down, so the agent +gets it right next time. + +## When to run this + +- A human edited a machine translation. +- A human marked a translation as human-reviewed. +- A human undid or rejected a lint fix or suggestion. +- A human rewrote a section that the fresh-user evaluation flagged. + +Look for these in commits to `i18n/` by humans, in reverts, and in review +comments on the agent's PRs. + +## Steps + +1. **Put the two versions side by side**: what the agent wrote and what the + human changed it to. Ignore changes that have nothing to do with the + agent's work. + +2. **Find the rule.** Ask: would this same correction apply somewhere else? + If you cannot describe it in a way that would apply to at least one other + page, it is a one-off. Skip it. + +3. **Put the rule in the right file.** + - The human changed a word or phrase, and would want it changed everywhere: + `glossary.yml`. If they put an English term back into a translation, add + that term with `translate: false`. + - The human changed how something is phrased in a translation (tone, + punctuation, how button names are handled): `translation-rules.yml`. + - The human rejected a lint finding: `style-exceptions.yml`. Scope it to + the one page unless they said it applies more widely. + - The human reverted an accuracy fix: no rule. That is a factual dispute. + Raise it as a question for the product team. + + Each file explains its own format at the top. Always record why the human + made the change (quote their review comment if there is one), their GitHub + handle, the date, and the PR. + +4. **Check for clashes.** Do not add a glossary term that already exists. Do + not add an exception so broad it switches a lint rule off everywhere. If a + new translation rule contradicts an old one, keep the new one and flag it. + +5. **Apply it where cheap.** A new glossary spelling can be fixed across + `docs/` right away. Leave other translations for the next translate run. + +6. **Open a PR** listing what was overridden, the rules you added, and any + questions. Rules only take effect once it is merged. + +Never edit the human's change. Never mark anything human-reviewed yourself. diff --git a/.agents/skills/fresh-user-eval.md b/.agents/skills/fresh-user-eval.md index 84ff3503bc17..50f4b0a2ed9d 100644 --- a/.agents/skills/fresh-user-eval.md +++ b/.agents/skills/fresh-user-eval.md @@ -1,32 +1,59 @@ -# Skill: Fresh-user evaluation - -Read one page as a new user and try to do what it says. Run after the -accuracy check. - -1. **Read cold.** Assume you know webhooks, APIs, JSON, and a terminal, and - nothing about OpenFn. Do not follow links or read git history. Write one - sentence on what the page teaches and for whom. If you cannot, that is a - finding. -2. **Do the task.** Procedural page: perform each step, confirming web app - elements exist in `OpenFn/lightning` and running CLI commands. Conceptual - page: explain it back in two sentences and answer three likely user - questions from the page alone. Reference page: use three entries from their - descriptions. Record a finding wherever you guessed an undefined term, got - stuck, hit an ambiguous sentence, needed an unstated prerequisite, found - steps out of order, or could not tell what success looks like. -3. **Verify guesses** against code or neighbouring pages. Wrong guesses are - strong findings; right guesses still count. - -Classify: *fix* for a single verified fact that fits one sentence; -*suggestion* for a new subsection, example, or rewrite; *question* when the -answer depends on audience or is unknown. Do not fix tone. - -Score, whole numbers, one sentence each: - -- **Readability 1 to 5**: 5 understood in one read; 3 gist only; 1 needs - outside knowledge. -- **Completeness 1 to 5**: 5 task done from this page alone; 3 done after - links or guesses; 1 page does not describe the task. - -Output: teaches, audience, what you attempted, findings in the shared format, -both scores. Do not evaluate generated adaptor pages or write new pages. +# Fresh-user evaluation + +Read one page as if you had never heard of OpenFn, then try to do what it +says. The point is to find where a newcomer would get lost. Run this after the +accuracy check, so the facts are already right. + +## Steps + +1. **Read it cold.** Pretend you know what an API, a webhook, JSON, and a + terminal are, and nothing else. Do not follow links. Do not look at the + page's history. When you finish, write one sentence saying what the page + teaches and who it is for. If you cannot, that is your first finding. + +2. **Try to do it.** + - If it is a how-to, follow the steps. For anything in the web app, check + the button or screen really exists in the Lightning code. For CLI steps, + run the commands. + - If it explains a concept, explain it back in two sentences. Then think of + three questions a newcomer would ask and see if the page answers them. + - If it is a reference table, pick three rows and see if you could use each + one from its description alone. + + Every time you have to guess what a word means, cannot find what the page + points at, read a sentence two ways, need something the page assumed you + had, find the steps in the wrong order, or cannot tell whether you + succeeded, write it down. + +3. **Check your guesses** against the code or nearby pages. If you guessed + wrong, the page definitely needs that information. If you guessed right, it + probably still does. + +## Sorting the findings + +- **Fix**: one missing fact that fits in one sentence and that you have + verified. Add it. +- **Suggestion**: anything bigger, such as a new subsection, an example, or a + rewrite. Propose the text but do not add it. +- **Question**: you could not find the answer, or it depends on who the page + is for. + +Do not touch the tone or voice. + +## Scores + +Give two scores from 1 to 5, each with a one-sentence reason. + +- **Readability.** 5: understood everything on one read. 3: got the gist but + had to work at it. 1: could not follow it without outside knowledge. +- **Completeness.** 5: could finish the task from this page alone and knew + when I was done. 3: got there, but only by following links or guessing. + 1: the page does not actually say how to do it. + +## What to report + +What the page teaches, who it is for, what you tried, your findings in the +standard format, and the two scores. + +Do not evaluate the generated adaptor pages. Do not write a new page; if the +page a user needs does not exist, that is a job for gap analysis. diff --git a/.agents/skills/gap-analysis.md b/.agents/skills/gap-analysis.md index a655d2f39f4e..9ce462f28b00 100644 --- a/.agents/skills/gap-analysis.md +++ b/.agents/skills/gap-analysis.md @@ -1,30 +1,52 @@ -# Skill: Gap analysis - -Compare what a section covers with what exists in the product and what users -ask. Rank the gaps. Write pages only if asked. - -1. **Inventory the docs**: what each page covers, from headings and tables. - Check that links out of the section point at pages that exist. -2. **Inventory the product** for the matching area: routes and LiveViews in - `OpenFn/lightning`; `packages/cli/src/commands.ts` and `openfn --help` in - `OpenFn/kit`; `packages/common/src/` exports in `OpenFn/adaptors`; - `DEPLOYMENT.md` and `config/runtime.exs` for deployment. -3. **Diff.** Search all of `docs/`, `articles/`, and `adaptors/*.md` before - calling something a gap. Label each: **missing page**, **partial page** - (right page exists, item absent), **misplaced**, or **stale** (hand to - `accuracy-check.md`). Note docs items the product no longer has. -4. **User signals**, only if accessible: `OpenFn/docs` issues, the community - forum, support channels, search analytics. Count, do not quote. Never - invent demand. -5. **Rank**: score 1 to 5 on reach, severity, evidence (5 repeated asks, 3 - prominent in UI, 1 code only), and effort (5 a paragraph, 1 a tutorial). - Sum and sort. - -Output per gap: label, score, what is missing, evidence, where it should live -(file, sidebar position or heading), suggested outline. Top ten in the PR; -collapse the rest. - -Only *fix*: a one-sentence cross-link to a page that clearly exists. -Everything else is a *suggestion*. Thin generated adaptor pages are JSDoc -issues, not gaps. Feature-flagged behaviour is a *question* for the product -team. +# Gap analysis + +Work out what a section of the docs should cover but does not. Produce a +ranked list. Do not write the missing pages unless you are asked to. + +## Steps + +1. **List what the docs cover.** For every page in the section, note what it + explains, using the headings and tables. Also note where it links out to + other sections, and check those pages exist. + +2. **List what the product has.** Look at the part of the code that matches + the section. For the web app, that is the routes and screens in + `OpenFn/lightning`. For the CLI, run `openfn --help` and look at the + commands in `OpenFn/kit`. For job writing, look at what + `packages/common` exports in `OpenFn/adaptors`. For deployment, read + `DEPLOYMENT.md` and the runtime config in Lightning. + +3. **Compare the two lists.** Before you call anything a gap, search the + whole docs folder, the articles, and the adaptor overviews. It might be + documented somewhere else. Label each gap as one of: + - **Missing page**: nothing in the docs mentions it. + - **Partial page**: the right page exists but does not cover this. + - **Misplaced**: it is documented, but not where a user would look. + - **Stale**: it describes an old version. Hand these to the accuracy check. + + Also note anything the docs describe that the product no longer has. + +4. **Look for user evidence**, if you have access to it: issues on the docs + repo, the community forum, support channels, search analytics. Count how + often a topic comes up. Do not quote anyone. If you have no access, say so, + and do not make up demand. + +5. **Rank.** Score each gap from 1 to 5 on four things: how many users it + affects, how bad it is to be without it, how much evidence you have that + people want it, and how easy it is to write (5 means a paragraph, 1 means + a whole tutorial). Add them up and sort. + +## What to report + +For each gap: its label, its score, what is missing, your evidence, where it +should go (which file, and where in the sidebar or which heading), and a +rough outline. Put the top ten in the PR and collapse the rest. + +## What you may change + +Only one thing: add a single sentence linking to a page that clearly already +covers the topic. Everything else is a suggestion. + +Thin generated adaptor pages are not gaps here; they are code-comment issues +for `OpenFn/adaptors`. If a feature is behind a feature flag, do not propose +documenting it. Ask the product team instead. diff --git a/.agents/skills/lint.md b/.agents/skills/lint.md index 48bb168c9641..6d558852ead0 100644 --- a/.agents/skills/lint.md +++ b/.agents/skills/lint.md @@ -1,27 +1,53 @@ -# Skill: Lint - -Deterministic checks on a docs section. Run first. Load `glossary.yml` and -`style-exceptions.yml`; skip findings matching an exception. Never touch -generated adaptor pages, `versioned_docs/`, or human-reviewed translations. - -Check prose only (not code, URLs, or front matter): - -1. **Terminology**: replace glossary `variants` with `term`; always fix - "adapter" → "adaptor". *Fix.* -2. **Headings**: no body H1, no skipped levels, no duplicates. *Fix.* Mixed - case is a *suggestion* (headings are anchors). -3. **Internal links**: must resolve. `yarn build` is authoritative - (`onBrokenLinks: throw`). *Fix* if the target is obvious, else *question*. -4. **External links**: 404/410 after two tries is dead. *Fix* if there is a - clear successor, else *suggestion*. 403/429 are uncertain, not dead. -5. **Orphans**: pages in no sidebar and linked from nowhere. *Suggestion.* -6. **Front matter**: valid YAML with `title`. *Fix* from the body H1. -7. **Code fences**: need a language (`js`, `json`, `yaml`, `bash`, `text`). *Fix.* -8. **Alt text**: must describe the image, not "screenshot". *Fix* from context. -9. **Admonitions**: blank line inside both `:::` fences. *Fix.* - -Change only the offending line. Run Prettier, then `yarn build`, before -committing. - -Output: counts, then one line per finding in the shared format. If a human -rejects a fix, hand it to `corrections-capture.md`. +# Lint + +Run this first on any section. These are mechanical checks. Most of what you +find, you can fix on the spot. + +Before you start, read `glossary.yml` (the approved terms) and +`style-exceptions.yml` (things humans have told us not to flag). Skip any +finding that matches an exception. + +Do not lint the generated adaptor pages, the old v1 docs, or translations +marked human-reviewed. + +## What to check + +Only look at prose. Ignore code blocks, URLs, and front matter. + +1. **Terminology.** If a page uses a spelling listed under `variants` in the + glossary, replace it with the approved term. Always change "adapter" to + "adaptor". Fix. +2. **Headings.** No `#` headings in the body (the title comes from front + matter). No jumping from `##` to `####`. No two headings with the same + text. Fix. If a page mixes Title Case and sentence case, suggest a change + rather than making it, because headings double as link anchors. +3. **Internal links.** Every link to another docs page must work. The easiest + way to check is `yarn build`, which fails on broken links. Fix it if the + right target is obvious. Otherwise ask. +4. **External links.** Try each one twice. A 404 or 410 means it is dead. + Replace it if there is a clear replacement; otherwise suggest removing it. + A 403 or 429 does not mean dead, so leave those alone and note them. +5. **Orphan pages.** A page that is not in any sidebar and not linked from + anywhere. Suggest adding or removing it. Do not decide yourself; someone + might be drafting it. +6. **Front matter.** Must be valid YAML and must have a `title`. If the title + is missing, take it from the page's first heading. Fix. +7. **Code blocks.** Every fenced block needs a language: `js`, `json`, + `yaml`, `bash`, or `text`. Fix. +8. **Alt text.** Every image needs alt text that says what the image shows. + "Screenshot" does not count. Write it from the surrounding paragraph. Fix. +9. **Callouts.** `:::tip` and similar blocks need a blank line after the + opening and before the closing. Fix. + +## How to fix + +Change only the line with the problem. When you are done, run Prettier on the +files you touched, then `yarn build`. Do not commit until both are clean. + +## What to report + +Give the counts (files checked, fixes, suggestions, questions), then one line +per finding in the standard format. + +If a reviewer later undoes one of your fixes, do not push back. Pass it to +`corrections-capture.md` so it becomes an exception. diff --git a/.agents/skills/screenshot-triage.md b/.agents/skills/screenshot-triage.md index 64c0e5d90732..60ed15b328d1 100644 --- a/.agents/skills/screenshot-triage.md +++ b/.agents/skills/screenshot-triage.md @@ -1,34 +1,56 @@ -# Skill: Screenshot triage - -Rank screenshots likely to be stale. Never retake, edit, or delete images. -Needs `OpenFn/lightning` cloned with history (`--filter=blob:none`). - -1. List `/img/...` references in the section (full triage: all of - `static/img/`, noting images no page uses). -2. Date each image with `git log -n 1 --format=%cs -- static/img/<file>`, - skipping bulk re-encode commits. -3. Map each image to a UI area from file name, alt text, and prose, with - confidence high/medium/low, then to Lightning paths: canvas - `assets/js/workflow-diagram/`; step editor `lib/lightning_web/live/ - workflow_live/`, `assets/js/collaborative-editor/`; runs and history - `run_live/`, `dataclip_live/`, `assets/js/log-viewer/`; credentials - `credential_live/`; project settings `project_live/`, `sandbox_live/`; - global styling `assets/css/app.css`, `lib/lightning_web/components/`. CLI - output maps to kit `packages/cli/src/`. Third-party UIs are `external`. -4. Date the UI: newest commit touching the mapped or global styling paths. - List user-visible commit subjects since the image date. -5. Suspect = UI newer than image. Rank by gap in days, then user-visible - commits, then Get Started and Tutorials first. Flag low-confidence - mappings in the top five. - -Output a table: image, page:line, image date, UI area (confidence), last UI -change, gap, what likely changed. List external, orphaned, and diagram images -separately. Top fifteen in the PR. The only edit allowed is correcting wrong -alt text. - -**Extension point, not implemented**: Lightning has Playwright specs in -`assets/test/e2e/specs/`. When they can emit docs screenshots, add step 6: -look each suspect up in a `screenshot-capture-map.yml` (image → spec, test -title, selector), run it with a capture flag against a seeded local -Lightning, write to `static/img/<same name>`, and present before/after as a -*suggestion*. +# Screenshot triage + +Find the screenshots most likely to be out of date and rank them so a human +can retake them. You never retake, edit, or delete an image yourself. + +You need a clone of `OpenFn/lightning` with full history, because the whole +method is about comparing dates. + +## Steps + +1. **List the images** the section uses (they are linked as `/img/...`). For + a whole-site triage, list everything in `static/img/` and note any image no + page uses. + +2. **Find out how old each image is** from its last commit in this repo. If + the last commit was a bulk optimisation that touched lots of images, look + at the one before it. + +3. **Work out what each image shows.** Use the file name, the alt text, and + the paragraph around it. Say how confident you are. Then match it to the + part of the Lightning code that draws that screen. Roughly: the workflow + canvas is under `assets/js/workflow-diagram`; the step editor, runs, + credentials, and project settings each have their own folder under + `lib/lightning_web/live/`; global styling is in `assets/css` and + `lib/lightning_web/components`. Screenshots of other products (Kobo, + DHIS2) have no matching code; mark them "external". + +4. **Find out when that part of the UI last changed.** Take the newest commit + touching the matching code, or the global styling, whichever is later. Skim + the commit messages since the image was taken and keep the ones that sound + visible to users (renamed, moved, redesigned, added a button). + +5. **Flag and rank.** An image is a suspect if the UI changed after it was + taken. Sort by the size of the gap, then by how many visible changes + happened in it, and give pages in Get Started and Tutorials a nudge up the + list. If you were not confident about what an image shows and it lands + near the top, say so. + +## What to report + +A table with: image, page and line, image date, what it shows and your +confidence, date of the last UI change, the gap in days, and what probably +changed. List external, unused, and diagram images separately. Put the top +fifteen in the PR and collapse the rest. + +The only edit you may make is correcting alt text that describes the image +wrongly. + +## Later: taking screenshots automatically + +Not built yet. Lightning already has Playwright browser tests under +`assets/test/e2e/specs/`. When those can produce screenshots, add a final +step: a mapping file that says which test reaches which screenshot, run the +test with a capture flag against a local Lightning, save the result over the +old image, and present the before-and-after in the PR as a suggestion for a +human to approve. diff --git a/.agents/skills/translate.md b/.agents/skills/translate.md index 890a212000c5..b6fd1003cfca 100644 --- a/.agents/skills/translate.md +++ b/.agents/skills/translate.md @@ -1,60 +1,87 @@ -# Skill: Translate +# Translate -Translate `docs/**` and `adaptors/*.md` into `es` and `fr`. English is -canonical. Output goes to -`i18n/<locale>/docusaurus-plugin-content-docs/current/<same path>` (adaptor -overviews: `...-content-docs-adaptors/current/`). Never translate -`adaptors/packages/**`, `adaptors/library/**`, or `versioned_docs/**`. +Translate English docs into Spanish (`es`) and French (`fr`). The English is +always the source of truth. Translations are generated files that live in +this repo, in the `i18n/` folder, mirroring the path of the English page. -## Preconditions (stop with a *question* if any fails) +Never translate the generated adaptor pages, the job library, or the old v1 +docs. -English section has no open fixes. `docusaurus.config.js` lists the locale -under `i18n.locales` (do not add it yourself). `/i18n` is not gitignored. -`glossary.yml` and `translation-rules.yml` parse. +## Before you start + +Check these four things. If any fails, stop and ask. + +- The English section has no unfinished fixes. Translating a page you are + about to change is wasted work. +- The locale is enabled in `docusaurus.config.js`. Do not enable it yourself; + that changes what gets deployed. +- `i18n/` is not in `.gitignore`. +- `glossary.yml` and `translation-rules.yml` are valid YAML. ## Front matter -Keep the source fields (translate `title` and `sidebar_label` only) and add: +Copy the English page's front matter. Translate only `title` and +`sidebar_label`. Then add: ```yaml -translation_source_hash: <git log -n 1 --format=%H -- <english file>> -translation_review_status: machine # machine | human-reviewed | needs-review -translation_reviewer: # human-reviewed only -translation_review_date: # human-reviewed only -translation_model: <your model id> +translation_source_hash: <the commit that last changed the English page> +translation_review_status: machine +translation_model: <the model you are running as> ``` -## Action +`translation_review_status` can be `machine`, `needs-review`, or +`human-reviewed`. Only a human ever sets `human-reviewed`, and when they do +they also add `translation_reviewer` and `translation_review_date`. + +## Decide what to do with each page + +- **No translation yet.** Translate the whole page. +- **Translation exists, status is `machine` or `needs-review`.** Translate the + whole page again, but keep any fenced blocks (see below) exactly as they + were. +- **Status is `human-reviewed` and the hash matches the current English + commit.** Skip it. It is up to date and approved. +- **Status is `human-reviewed` and the hash is older.** Do not touch the + file. Work out what changed in the English since that hash, translate only + those parts, and open a separate PR with the proposed diff for the named + reviewer. -| Exists? | Status | Hash current? | Action | -| ------- | ----------------------- | ------------- | ---------------------------------------- | -| no | | | Full translation | -| yes | machine / needs-review | any | Regenerate, preserving fences | -| yes | human-reviewed | yes | Skip | -| yes | human-reviewed | no | Translate only the changed English hunks; open a separate PR with the diff for `translation_reviewer`. Never write the file. | +## Fenced blocks + +A human can wrap part of a translation like this: + +```markdown +<!-- do-not-retranslate --> +Text a reviewer has corrected by hand. +<!-- /do-not-retranslate --> +``` -Fences: content between `<!-- do-not-retranslate -->` and -`<!-- /do-not-retranslate -->` is copied byte for byte at the same position. -If its English source is gone, keep it and raise a *question*. +Copy those blocks into the new translation exactly, in the same place. If the +English they correspond to has been deleted, keep the block anyway and ask +what to do with it. -## Rules +## How to translate -- Glossary terms with `translate: false` and `patterns` stay verbatim. - `product_noun` terms stay only where they name the OpenFn concept. -- Apply the locale's `translation-rules.yml` rules. Default: Spanish "tú", - French "vous". -- Code blocks and inline code are copied byte for byte (prose comments may be - translated). Markdown and MDX structure stays identical. -- Prefix internal links with `/<locale>`, except links into - `adaptors/packages/`. Add `{#original-anchor}` to translated headings. +- Words in `glossary.yml` stay in English. For ordinary words that are also + product terms, like "run" or "step", keep the English only when the word + means the OpenFn thing. +- Follow any rules for the locale in `translation-rules.yml`. By default, + Spanish uses "tú" and French uses "vous". +- Copy code blocks and inline code exactly. You may translate comments inside + code. +- Keep the same structure: same headings at the same levels, same lists, + same callouts, same components. +- Add the locale to internal links (`/es/documentation/...`), except links + into the generated adaptor pages, which are English only. Give translated + headings the original English anchor so existing links still work. -## Checks before committing +## Before you commit -Glossary terms appear as often as in the source. Code blocks identical. Same -counts of headings, fences, admonitions, images, tables. All front matter -fields present. Every old fence preserved. `yarn docusaurus build --locale -<locale>` passes. +Check that every glossary term appears as many times as in the English. Check +the code blocks are identical. Check the counts of headings, code blocks, +callouts, images, and tables match. Check the front matter is complete. Check +every fenced block survived. Then build that locale and make sure it passes. -Commit per locale (`translate(es): <section>`). Each file counts toward the -20-file limit. Never set `human-reviewed`; never improve the English while -translating. +Commit one locale at a time. Each translated file counts toward the 20-file +limit. If you spot a problem in the English while translating, note it for +the next English pass; do not fix it here. diff --git a/AGENTS.md b/AGENTS.md index 8c0dda4eab2d..f55bd20fef34 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,65 +1,111 @@ -# AGENTS.md: docs maintenance agent for OpenFn/docs +# Docs maintenance agent -You maintain the OpenFn docs (Docusaurus 3), one section at a time. Skills -live in `.agents/skills/`; each is self-contained. +You look after the OpenFn documentation site. It is a Docusaurus project. You +work on one section of the docs at a time, and your job is to make that +section accurate, easy to follow, complete, and (once the English is right) +translated. -## Repo map +The detailed instructions for each job live in `.agents/skills/`. Each one +stands alone; read the one you need. -- `docs/**`, `sidebars-main.js`, `adaptors/*.md`: editable English source. -- `adaptors/packages/**`, `adaptors/library/**`: generated at build time from - JSDoc in `OpenFn/adaptors`. Never edit; fix upstream. -- `versioned_docs/**`: frozen v1 docs. Never edit. -- `i18n/<locale>/**`: translations, governed by `translate.md`. -- `glossary.yml`, `style-exceptions.yml`, `translation-rules.yml`: rules. -- `docusaurus.config.js`, `package.json`, `.github/`: ask before editing. +## What you can and cannot edit -Product code for verification, read-only, cloned outside this repo: -`OpenFn/lightning` (web app), `OpenFn/kit` (CLI, runtime), `OpenFn/adaptors`. +**Edit freely** -## Order +- Everything in `docs/`. This is the English source of truth. +- `sidebars-main.js`, which controls the navigation. +- The adaptor overview pages in `adaptors/*.md`. -lint → accuracy-check → fresh-user-eval → gap-analysis → screenshot-triage -(if images) → translate (only when the English has no open fixes or -questions). Run `corrections-capture` whenever a human has overridden agent -output. If the user names one skill, run only that. +**Do not edit** -## Scope +- Anything in `adaptors/packages/` or `adaptors/library/`. These pages are + built automatically from code comments in the `OpenFn/adaptors` repo. If + something is wrong there, the fix belongs in that repo, not here. +- Anything in `versioned_docs/`. These are the old v1 docs and are frozen. -A section is one `sidebars-main.js` category, one `docs/` directory, or one -page. Never the whole site. No section named: ask, listing the categories. +**Ask before editing** -## Findings +- `docusaurus.config.js`, `package.json`, and anything in `.github/`. These + change how the site builds and deploys. -- **fix**: objectively wrong, correct value known from code or build. Apply, - locally. Never rewrite voice or structure as a "fix". -- **suggestion**: a judgement call. Record in the PR with proposed text. -- **question**: docs and code disagree, or the decision is not yours. Ask. +**Special rules apply** -When in doubt, downgrade. +- Translations in `i18n/`. See `translate.md`. +- The three rule files: `glossary.yml`, `style-exceptions.yml`, + `translation-rules.yml`. See `corrections-capture.md`. -Format: `[fix|suggestion|question] <file>:<line> — <problem> — <action>` +To check facts, you can read the product code. Clone `OpenFn/lightning` (the +web app), `OpenFn/kit` (the CLI), and `OpenFn/adaptors` somewhere outside this +repo. Never change them. -## Hard rules +## The order of work -Never edit a page with `translation_review_status: human-reviewed` (suggest a -diff). Never edit generated adaptor pages (draft an `OpenFn/adaptors` issue; -file only if asked). Never retranslate inside `<!-- do-not-retranslate -->` -fences. Never translate glossary terms. Never retake screenshots. Never change -build config without asking. Never disable a check to get green. +1. **Lint.** Fix formatting, links, headings, and terminology. +2. **Accuracy check.** Make sure every claim matches the code. +3. **Fresh-user evaluation.** Read the page as a newcomer and see if it works. +4. **Gap analysis.** Work out what is missing from the section. +5. **Screenshot triage**, if the section has images. +6. **Translate**, but only when steps 1 to 3 left nothing open. -## Stopping and the PR +Run **corrections capture** any time a human has overridden something the +agent did earlier. -Stop when the section is done or after **20 changed files**, whichever comes -first. Before the PR: `npx prettier --write` on changed files, `yarn build` -(broken links fail it), re-read the diff. Branch `docs-agent/<section>`. +If the user asks for one skill only, run that one and still finish with a PR. -Use `.github/pull_request_template.md`, tick "I have used Claude Code", and -add: what changed, suggestions, questions, skipped (human-reviewed, generated, -not reached), upstream issues, scores, gaps, suspect screenshots. Omit empty -sections. +## Pick one section -## Conventions +A section is one category from the sidebar, one folder under `docs/`, or one +page. Never work on the whole site at once. If the user has not said which +section, stop and ask. List the sidebar categories to make choosing easy. -Front matter needs `title`. Internal links are site-absolute -(`/documentation/...`). Images are `/img/<file>` in `static/img/`. Spelling is -**adaptor**, never "adapter". Terms are in `glossary.yml`. +## Three kinds of finding + +Everything you notice falls into one of three buckets: + +- **Fix.** It is clearly wrong and you know the right answer from the code or + the build. Make the change. Keep it small. Do not rewrite a page's voice or + structure and call it a fix. +- **Suggestion.** It is a judgement call. Do not change it. Write up what you + would change and why in the PR description, so a human can decide. +- **Question.** The docs and the code disagree and you cannot tell which is + right, or the decision is not yours to make. Do not guess. Ask. + +If you are unsure which bucket something belongs in, pick the more cautious +one. + +Write findings like this: + +``` +[fix] docs/build/triggers.md:42 — flag is called --force, not -f — corrected +``` + +## Rules that never bend + +- Never edit a translated page marked `translation_review_status: + human-reviewed`. Offer a diff instead. +- Never edit generated adaptor pages. Draft an issue for `OpenFn/adaptors` + and put it in the PR. Only file it if asked. +- Never retranslate text inside `<!-- do-not-retranslate -->` fences. +- Never translate a term listed in `glossary.yml`. +- Never retake, crop, or replace screenshots. +- Never disable a check to make the build pass. + +## When to stop + +Stop when the section is finished, or when you have changed 20 files, +whichever comes first. Then open a PR. + +Before you open it: run Prettier on the files you changed, run `yarn build` +(a broken link will fail the build), and read your own diff once more. + +Use the PR template in `.github/`. Tick "I have used Claude Code". Then add +sections for: what changed, suggestions, questions, what you skipped and why, +upstream issues, scores, gaps, and suspect screenshots. Leave out any that +are empty. + +## House style + +- Every page has a `title` in its front matter. +- Internal links start with `/documentation/`, `/adaptors/`, or `/articles/`. +- Images live in `static/img/` and are linked as `/img/filename`. +- It is spelled **adaptor**, never "adapter". From 6d2fb24b89ff16ac6b0c35f021a8e5550b103185 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 12:27:42 +0000 Subject: [PATCH 05/13] Remove corrections-capture skill The three rule files stay and are maintained by humans directly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/corrections-capture.md | 52 --------------------------- .agents/skills/lint.md | 4 +-- AGENTS.md | 8 ++--- glossary.yml | 4 +-- style-exceptions.yml | 5 ++- translation-rules.yml | 5 ++- 6 files changed, 11 insertions(+), 67 deletions(-) delete mode 100644 .agents/skills/corrections-capture.md diff --git a/.agents/skills/corrections-capture.md b/.agents/skills/corrections-capture.md deleted file mode 100644 index bfade7bca140..000000000000 --- a/.agents/skills/corrections-capture.md +++ /dev/null @@ -1,52 +0,0 @@ -# Corrections capture - -When a human changes something the agent did, do not treat it as a one-off. -Work out the general rule behind it and write that rule down, so the agent -gets it right next time. - -## When to run this - -- A human edited a machine translation. -- A human marked a translation as human-reviewed. -- A human undid or rejected a lint fix or suggestion. -- A human rewrote a section that the fresh-user evaluation flagged. - -Look for these in commits to `i18n/` by humans, in reverts, and in review -comments on the agent's PRs. - -## Steps - -1. **Put the two versions side by side**: what the agent wrote and what the - human changed it to. Ignore changes that have nothing to do with the - agent's work. - -2. **Find the rule.** Ask: would this same correction apply somewhere else? - If you cannot describe it in a way that would apply to at least one other - page, it is a one-off. Skip it. - -3. **Put the rule in the right file.** - - The human changed a word or phrase, and would want it changed everywhere: - `glossary.yml`. If they put an English term back into a translation, add - that term with `translate: false`. - - The human changed how something is phrased in a translation (tone, - punctuation, how button names are handled): `translation-rules.yml`. - - The human rejected a lint finding: `style-exceptions.yml`. Scope it to - the one page unless they said it applies more widely. - - The human reverted an accuracy fix: no rule. That is a factual dispute. - Raise it as a question for the product team. - - Each file explains its own format at the top. Always record why the human - made the change (quote their review comment if there is one), their GitHub - handle, the date, and the PR. - -4. **Check for clashes.** Do not add a glossary term that already exists. Do - not add an exception so broad it switches a lint rule off everywhere. If a - new translation rule contradicts an old one, keep the new one and flag it. - -5. **Apply it where cheap.** A new glossary spelling can be fixed across - `docs/` right away. Leave other translations for the next translate run. - -6. **Open a PR** listing what was overridden, the rules you added, and any - questions. Rules only take effect once it is merged. - -Never edit the human's change. Never mark anything human-reviewed yourself. diff --git a/.agents/skills/lint.md b/.agents/skills/lint.md index 6d558852ead0..e640fb219a8c 100644 --- a/.agents/skills/lint.md +++ b/.agents/skills/lint.md @@ -49,5 +49,5 @@ files you touched, then `yarn build`. Do not commit until both are clean. Give the counts (files checked, fixes, suggestions, questions), then one line per finding in the standard format. -If a reviewer later undoes one of your fixes, do not push back. Pass it to -`corrections-capture.md` so it becomes an exception. +If a reviewer undoes one of your fixes, do not push back. Suggest they add an +entry to `style-exceptions.yml` so it is not flagged again. diff --git a/AGENTS.md b/AGENTS.md index f55bd20fef34..833e14005880 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,8 +31,9 @@ stands alone; read the one you need. **Special rules apply** - Translations in `i18n/`. See `translate.md`. -- The three rule files: `glossary.yml`, `style-exceptions.yml`, - `translation-rules.yml`. See `corrections-capture.md`. +- The three rule files: `glossary.yml`, `style-exceptions.yml`, and + `translation-rules.yml`. Humans maintain these. Each explains its format at + the top. Only add an entry if the user asks you to. To check facts, you can read the product code. Clone `OpenFn/lightning` (the web app), `OpenFn/kit` (the CLI), and `OpenFn/adaptors` somewhere outside this @@ -47,9 +48,6 @@ repo. Never change them. 5. **Screenshot triage**, if the section has images. 6. **Translate**, but only when steps 1 to 3 left nothing open. -Run **corrections capture** any time a human has overridden something the -agent did earlier. - If the user asks for one skill only, run that one and still finish with a PR. ## Pick one section diff --git a/glossary.yml b/glossary.yml index da5ec6dbb98f..f15f801eaa04 100644 --- a/glossary.yml +++ b/glossary.yml @@ -9,8 +9,8 @@ # 2. The lint skill (.agents/skills/lint.md). Any spelling in `variants` # is flagged in English pages and replaced with `term`. # -# The corrections-capture skill appends new entries when a human edit implies -# a terminology rule. Humans can edit this file directly too. +# Humans maintain this file. Add a term when a review shows the same +# correction being made more than once. # # Schema # ------ diff --git a/style-exceptions.yml b/style-exceptions.yml index 5669dc06b6fd..563597b2f443 100644 --- a/style-exceptions.yml +++ b/style-exceptions.yml @@ -6,9 +6,8 @@ # (.agents/skills/lint.md) loads this file and suppresses any finding that # matches an entry, so the same rejected suggestion is not raised again. # -# Entries are added by the corrections-capture skill -# (.agents/skills/corrections-capture.md) when a reviewer rejects or reverts a -# lint change in a PR, or by humans directly. +# Humans maintain this file. Add an entry when you reject or revert a lint +# change in a PR and do not want it raised again. # # Schema # ------ diff --git a/translation-rules.yml b/translation-rules.yml index a28e55ad3ab6..07a70955629a 100644 --- a/translation-rules.yml +++ b/translation-rules.yml @@ -10,9 +10,8 @@ # Glossary terms (never translate) belong in glossary.yml, not here. This file # is for how to translate, not what to leave alone. # -# Entries are added by the corrections-capture skill -# (.agents/skills/corrections-capture.md) when a reviewer edits a translated -# page and the edit implies a general pattern, or by humans directly. +# Humans maintain this file. Add a rule when you correct a translation in a +# way that should apply to other pages too. # # Schema # ------ From 9b70b8aa64203cb753d9c843dfaceb0535da32b7 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 12:40:45 +0000 Subject: [PATCH 06/13] Translate skill: drop fix precondition, handle missing status, narrow glossary check, exempt from file cap Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/translate.md | 23 +++++++++++++++-------- AGENTS.md | 5 +++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.agents/skills/translate.md b/.agents/skills/translate.md index b6fd1003cfca..8548b800a038 100644 --- a/.agents/skills/translate.md +++ b/.agents/skills/translate.md @@ -9,15 +9,16 @@ docs. ## Before you start -Check these four things. If any fails, stop and ask. +Check these three things. If any fails, stop and ask. -- The English section has no unfinished fixes. Translating a page you are - about to change is wasted work. - The locale is enabled in `docusaurus.config.js`. Do not enable it yourself; that changes what gets deployed. - `i18n/` is not in `.gitignore`. - `glossary.yml` and `translation-rules.yml` are valid YAML. +If you changed any English pages earlier in this run, commit them before you +translate, so the source hash points at the version you actually translated. + ## Front matter Copy the English page's front matter. Translate only `title` and @@ -39,6 +40,8 @@ they also add `translation_reviewer` and `translation_review_date`. - **Translation exists, status is `machine` or `needs-review`.** Translate the whole page again, but keep any fenced blocks (see below) exactly as they were. +- **Translation exists but has no `translation_review_status`.** Treat it as + `machine` and regenerate it. - **Status is `human-reviewed` and the hash matches the current English commit.** Skip it. It is up to date and approved. - **Status is `human-reviewed` and the hash is older.** Do not touch the @@ -77,11 +80,15 @@ what to do with it. ## Before you commit -Check that every glossary term appears as many times as in the English. Check -the code blocks are identical. Check the counts of headings, code blocks, +Check that the fixed glossary terms (the ones without `product_noun: true`, +such as OpenFn, Lightning, adaptor, webhook) appear as many times as in the +English. Product nouns like "run" and "step" are allowed to differ, since +their ordinary-English uses get translated. Check the code blocks are +identical. Check the counts of headings, code blocks, callouts, images, and tables match. Check the front matter is complete. Check every fenced block survived. Then build that locale and make sure it passes. -Commit one locale at a time. Each translated file counts toward the 20-file -limit. If you spot a problem in the English while translating, note it for -the next English pass; do not fix it here. +Open one PR per locale per section, separate from the English PR. Translated +files do not count toward the 20-file limit, because a section's translations +are reviewed as a set. If you spot a problem in the English while translating, +note it for the next English pass; do not fix it here. diff --git a/AGENTS.md b/AGENTS.md index 833e14005880..172a6eaa9d15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,7 +46,7 @@ repo. Never change them. 3. **Fresh-user evaluation.** Read the page as a newcomer and see if it works. 4. **Gap analysis.** Work out what is missing from the section. 5. **Screenshot triage**, if the section has images. -6. **Translate**, but only when steps 1 to 3 left nothing open. +6. **Translate**, in its own PR per locale. If the user asks for one skill only, run that one and still finish with a PR. @@ -91,7 +91,8 @@ Write findings like this: ## When to stop Stop when the section is finished, or when you have changed 20 files, -whichever comes first. Then open a PR. +whichever comes first. Then open a PR. Translations are the exception: they go +in their own PR per locale and do not count toward the 20. Before you open it: run Prettier on the files you changed, run `yarn build` (a broken link will fail the build), and read your own diff once more. From 80a924e8c17334602fb38855ae98c72286325401 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 12:59:38 +0000 Subject: [PATCH 07/13] Normalise line wrapping before matching multi-word glossary terms Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/lint.md | 4 +++- .agents/skills/translate.md | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.agents/skills/lint.md b/.agents/skills/lint.md index e640fb219a8c..d4fa4bd812b2 100644 --- a/.agents/skills/lint.md +++ b/.agents/skills/lint.md @@ -16,7 +16,9 @@ Only look at prose. Ignore code blocks, URLs, and front matter. 1. **Terminology.** If a page uses a spelling listed under `variants` in the glossary, replace it with the approved term. Always change "adapter" to - "adaptor". Fix. + "adaptor". Fix. Multi-word terms can be split across a line break by + Prettier's wrapping, so match against text with newlines collapsed to + spaces. 2. **Headings.** No `#` headings in the body (the title comes from front matter). No jumping from `##` to `####`. No two headings with the same text. Fix. If a page mixes Title Case and sentence case, suggest a change diff --git a/.agents/skills/translate.md b/.agents/skills/translate.md index 8548b800a038..5596cd32aa3c 100644 --- a/.agents/skills/translate.md +++ b/.agents/skills/translate.md @@ -83,8 +83,11 @@ what to do with it. Check that the fixed glossary terms (the ones without `product_noun: true`, such as OpenFn, Lightning, adaptor, webhook) appear as many times as in the English. Product nouns like "run" and "step" are allowed to differ, since -their ordinary-English uses get translated. Check the code blocks are -identical. Check the counts of headings, code blocks, +their ordinary-English uses get translated. Before counting, join each file +into one line with single spaces: Prettier wraps prose at 80 columns, and +English and Spanish wrap at different points, so a multi-word term like "work +order" can sit across a line break in one file and not the other. Check the +code blocks are identical. Check the counts of headings, code blocks, callouts, images, and tables match. Check the front matter is complete. Check every fenced block survived. Then build that locale and make sure it passes. From c071e677e420d48a87316011ad8bf8685074ae93 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 13:37:37 +0000 Subject: [PATCH 08/13] Add PR analysis skill Starts from a product PR and finds the docs pages that need updating. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/pr-analysis.md | 68 +++++++++++++++++++++++++++++++++++ AGENTS.md | 5 +++ 2 files changed, 73 insertions(+) create mode 100644 .agents/skills/pr-analysis.md diff --git a/.agents/skills/pr-analysis.md b/.agents/skills/pr-analysis.md new file mode 100644 index 000000000000..0b60d38356df --- /dev/null +++ b/.agents/skills/pr-analysis.md @@ -0,0 +1,68 @@ +# PR analysis + +Read a pull request in one of the product repos and make sure the docs still +describe what the product does after it merges. Run this when someone gives +you a PR, or a range of commits or a release tag, from `OpenFn/lightning`, +`OpenFn/kit`, or `OpenFn/adaptors`. + +This is the inverse of the accuracy check. That skill starts from a docs page +and looks for the code. This one starts from a code change and looks for the +docs. + +## Steps + +1. **Read the PR.** Start with the title, description, and any linked issue, + then the diff. Write down, in plain terms, what changed for a user: a new + feature, a renamed button, a new CLI flag, a changed default, a removed + option, a different error message, a new config value. Ignore anything a + user would never see: refactors, tests, dependency bumps, internal renames. + If nothing user-facing changed, say so and stop. + +2. **Find the docs that talk about it.** For each user-facing change, search + the docs for the feature, the old and new names, the flag, the setting, and + any screenshots of that screen. Search `docs/`, `articles/`, and + `adaptors/*.md`. Note every page and line that mentions it. + +3. **Decide what each page needs.** Go through the mentions and sort them: + - **Now wrong.** The docs describe the old behaviour. This is a fix if the + new behaviour is clear from the diff and slots into the existing + sentence (a renamed flag, a changed default). Otherwise it is a + suggestion with proposed wording. + - **Now incomplete.** The page is still right but does not mention the new + thing. Suggest where the new paragraph or table row should go and draft + it. + - **Nothing in the docs.** A new feature with no home yet. Suggest which + page or section it belongs in and give a short outline. Do not write the + page unless asked. + - **Screenshot affected.** The change alters a screen that appears in an + image. List the image so it goes on the retake list. Never retake it. + +4. **Check the PR's own docs claims.** If the PR description says "docs + updated" or links a docs PR, check that what it says matches what the diff + does. If the PR touches user-facing text in the app (button labels, error + messages, help text), search the docs for the old text. + +5. **Be careful about timing.** If the PR is not merged yet, say so at the top + of your report, and do not change any docs page. Docs should describe what + is released. Write everything up as suggestions and note which release the + change is expected in. + +## Adaptor PRs + +Changes to function signatures and descriptions in `OpenFn/adaptors` flow +into the docs automatically through the generated reference pages. You do +not need to do anything for those. Look only at the hand-written overview +page (`adaptors/<name>.md`) and at any tutorial or guide that uses the +changed function. If the PR changes a function's behaviour but not its code +comment, that is an upstream issue for the adaptors repo, not a docs fix. + +## What to report + +Start with one paragraph: what the PR does for users, whether it is merged, +and how many docs pages are affected. Then the findings in the standard +format, grouped by page. Finish with the images that need retaking and any +new pages that are needed. + +If you were asked to make the changes and the PR is merged, apply the fixes, +run Prettier and `yarn build`, and open a docs PR that links back to the +product PR. Everything else goes in the PR description as suggestions. diff --git a/AGENTS.md b/AGENTS.md index 172a6eaa9d15..f2f77a56ef66 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,6 +50,11 @@ repo. Never change them. If the user asks for one skill only, run that one and still finish with a PR. +One skill runs from the other direction. **PR analysis** starts from a pull +request in a product repo and finds the docs pages that need to change +because of it. Run it when someone hands you a product PR, a commit range, or +a release tag. + ## Pick one section A section is one category from the sidebar, one folder under `docs/`, or one From 651a88328571f6b51ef20f83c5457147bf73a1bc Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 13:38:47 +0000 Subject: [PATCH 09/13] PR analysis: spell out the cross-repo setup Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/pr-analysis.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/.agents/skills/pr-analysis.md b/.agents/skills/pr-analysis.md index 0b60d38356df..226d45a70141 100644 --- a/.agents/skills/pr-analysis.md +++ b/.agents/skills/pr-analysis.md @@ -9,6 +9,23 @@ This is the inverse of the accuracy check. That skill starts from a docs page and looks for the code. This one starts from a code change and looks for the docs. +## Two repos are involved + +The PR lives in a product repo. The docs live here. You need both checked +out. + +- **If you are running in the docs repo** and someone gives you a PR link, + clone the product repo into a scratch directory outside this one, then + fetch the PR: `git fetch origin pull/<number>/head:pr-<number>`. Diff it + against the base branch. Do not modify the product repo. +- **If you are running inside Lightning, kit, or adaptors** (for example, + someone on a product PR asks "does this need a docs change?"), clone + `OpenFn/docs` into a scratch directory and follow the same steps. Any docs + changes go in a branch and PR on the docs repo, never in the product PR. + +Either way, name the product repo, PR number, and head commit at the top of +your report so a reader knows exactly what you looked at. + ## Steps 1. **Read the PR.** Start with the title, description, and any linked issue, @@ -63,6 +80,9 @@ and how many docs pages are affected. Then the findings in the standard format, grouped by page. Finish with the images that need retaking and any new pages that are needed. -If you were asked to make the changes and the PR is merged, apply the fixes, -run Prettier and `yarn build`, and open a docs PR that links back to the -product PR. Everything else goes in the PR description as suggestions. +If you were asked to make the changes and the PR is merged, apply the fixes +in the docs repo, run Prettier and `yarn build`, and open a docs PR that links +back to the product PR. Everything else goes in that PR's description as +suggestions. If you were invoked from the product PR, leave one comment there +linking to the docs PR or summarising the findings, so the product reviewer +can see the docs were considered. From 8ae2859dee9bf3455721c48b85fe9a7260f24afc Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 14:22:09 +0000 Subject: [PATCH 10/13] Translate: use a content hash for translation_source_hash Commit SHAs made on a branch dangle after a squash merge. A git blob hash is the same wherever the file lives and still lets a reviewer recover the English they approved. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/translate.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.agents/skills/translate.md b/.agents/skills/translate.md index 5596cd32aa3c..3e96a37082bd 100644 --- a/.agents/skills/translate.md +++ b/.agents/skills/translate.md @@ -16,8 +16,8 @@ Check these three things. If any fails, stop and ask. - `i18n/` is not in `.gitignore`. - `glossary.yml` and `translation-rules.yml` are valid YAML. -If you changed any English pages earlier in this run, commit them before you -translate, so the source hash points at the version you actually translated. +Translate the English page as it is on disk after any fixes and after +Prettier has run, so the hash you record matches what you translated. ## Front matter @@ -25,11 +25,19 @@ Copy the English page's front matter. Translate only `title` and `sidebar_label`. Then add: ```yaml -translation_source_hash: <the commit that last changed the English page> +translation_source_hash: <git hash-object of the English file> translation_review_status: machine translation_model: <the model you are running as> ``` +The hash is the content hash of the English file, from +`git hash-object docs/<path>.md`, not a commit. Commits do not survive squash +merges: a hash pointing at a commit made on a branch dangles as soon as the +branch is squashed onto main. A content hash is the same wherever the file +lives, and it answers the only question the field exists to answer: is the +English still the version this was translated from? To compare, hash the +current English file and check it against the recorded value. + `translation_review_status` can be `machine`, `needs-review`, or `human-reviewed`. Only a human ever sets `human-reviewed`, and when they do they also add `translation_reviewer` and `translation_review_date`. @@ -43,11 +51,13 @@ they also add `translation_reviewer` and `translation_review_date`. - **Translation exists but has no `translation_review_status`.** Treat it as `machine` and regenerate it. - **Status is `human-reviewed` and the hash matches the current English - commit.** Skip it. It is up to date and approved. -- **Status is `human-reviewed` and the hash is older.** Do not touch the - file. Work out what changed in the English since that hash, translate only - those parts, and open a separate PR with the proposed diff for the named - reviewer. + file.** Skip it. It is up to date and approved. +- **Status is `human-reviewed` and the hash no longer matches.** Do not touch + the file. Recover the English the reviewer saw with + `git cat-file -p <recorded hash>`, diff it against the current English, + translate only the changed parts, and open a separate PR with the proposed + diff for the named reviewer. If the old blob is no longer in the repo, + say so and offer a full retranslation as the suggested diff instead. ## Fenced blocks From 437318d88c76c30653982e50df1ce35e067f502e Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 14:25:28 +0000 Subject: [PATCH 11/13] Screenshot triage: whole-repo scan by default, with a cached classification map Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/screenshot-triage.md | 34 ++++++++++++++++++++++------- AGENTS.md | 3 ++- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/.agents/skills/screenshot-triage.md b/.agents/skills/screenshot-triage.md index 60ed15b328d1..91c580214326 100644 --- a/.agents/skills/screenshot-triage.md +++ b/.agents/skills/screenshot-triage.md @@ -3,22 +3,39 @@ Find the screenshots most likely to be out of date and rank them so a human can retake them. You never retake, edit, or delete an image yourself. +By default, scan the whole repo. A ranking only means something across the +whole site. If the user names a page or section, limit the scan to the images +those pages use. + You need a clone of `OpenFn/lightning` with full history, because the whole method is about comparing dates. +## Remembering what each image shows + +Keep a file called `screenshot-map.yml` at the repo root. For each image it +records what the image shows, which UI area that maps to, and how confident +you were. Read it at the start of every run. Only classify images that are +new, renamed, or missing from the file, then add them. Dates are always +recomputed; classifications are not. This makes a repeat scan of the whole +repo cheap. + +Humans can edit this file to correct a classification, and the correction +sticks. Put a short comment at the top explaining the format. + ## Steps -1. **List the images** the section uses (they are linked as `/img/...`). For - a whole-site triage, list everything in `static/img/` and note any image no - page uses. +1. **List the images.** For the whole repo, everything in `static/img/`, + noting any image no page uses. For a page or section, only the images + those pages link as `/img/...`. 2. **Find out how old each image is** from its last commit in this repo. If the last commit was a bulk optimisation that touched lots of images, look at the one before it. -3. **Work out what each image shows.** Use the file name, the alt text, and - the paragraph around it. Say how confident you are. Then match it to the - part of the Lightning code that draws that screen. Roughly: the workflow +3. **Work out what each image shows**, for images not already in + `screenshot-map.yml`. Use the file name, the alt text, and the paragraph + around it. Say how confident you are. Then match it to the part of the + Lightning code that draws that screen. Roughly: the workflow canvas is under `assets/js/workflow-diagram`; the step editor, runs, credentials, and project settings each have their own folder under `lib/lightning_web/live/`; global styling is in `assets/css` and @@ -43,8 +60,9 @@ confidence, date of the last UI change, the gap in days, and what probably changed. List external, unused, and diagram images separately. Put the top fifteen in the PR and collapse the rest. -The only edit you may make is correcting alt text that describes the image -wrongly. +Whole-repo scans are report-only apart from updating `screenshot-map.yml`. +When scoped to a page or section, you may also correct alt text that describes +an image wrongly. ## Later: taking screenshots automatically diff --git a/AGENTS.md b/AGENTS.md index f2f77a56ef66..cfa22812bc7c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,7 +45,8 @@ repo. Never change them. 2. **Accuracy check.** Make sure every claim matches the code. 3. **Fresh-user evaluation.** Read the page as a newcomer and see if it works. 4. **Gap analysis.** Work out what is missing from the section. -5. **Screenshot triage**, if the section has images. +5. **Screenshot triage.** Scans the whole repo by default; runs on request + rather than every time. 6. **Translate**, in its own PR per locale. If the user asks for one skill only, run that one and still finish with a PR. From 4b87909400c2fcad291cd1c5db1bafe972022034 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 14:29:52 +0000 Subject: [PATCH 12/13] Gap analysis: read CLI source not --help, exempt hidden commands, search invocations, separate ease from impact Based on a test run against the CLI section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/gap-analysis.md | 41 ++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/.agents/skills/gap-analysis.md b/.agents/skills/gap-analysis.md index 9ce462f28b00..b95d1df7b590 100644 --- a/.agents/skills/gap-analysis.md +++ b/.agents/skills/gap-analysis.md @@ -11,14 +11,22 @@ ranked list. Do not write the missing pages unless you are asked to. 2. **List what the product has.** Look at the part of the code that matches the section. For the web app, that is the routes and screens in - `OpenFn/lightning`. For the CLI, run `openfn --help` and look at the - commands in `OpenFn/kit`. For job writing, look at what + `OpenFn/lightning`. For the CLI, read `packages/cli/src/cli.ts` and + `commands.ts` in `OpenFn/kit`; that is where commands are registered, and + it shows things `--help` hides. Use `openfn --help` only as a fallback if + the CLI happens to be installed. For job writing, look at what `packages/common` exports in `OpenFn/adaptors`. For deployment, read `DEPLOYMENT.md` and the runtime config in Lightning. + If you cannot reach the repo you need, say so in the report and skip that + part. Do not fill the gap from memory. + 3. **Compare the two lists.** Before you call anything a gap, search the whole docs folder, the articles, and the adaptor overviews. It might be - documented somewhere else. Label each gap as one of: + documented somewhere else. Search for the thing as a user would type it, + not the bare noun: "openfn metadata" settles the question in one hit, + while "metadata" matches twenty pages of ordinary prose. Label each gap + as one of: - **Missing page**: nothing in the docs mentions it. - **Partial page**: the right page exists but does not cover this. - **Misplaced**: it is documented, but not where a user would look. @@ -31,16 +39,23 @@ ranked list. Do not write the missing pages unless you are asked to. often a topic comes up. Do not quote anyone. If you have no access, say so, and do not make up demand. -5. **Rank.** Score each gap from 1 to 5 on four things: how many users it - affects, how bad it is to be without it, how much evidence you have that - people want it, and how easy it is to write (5 means a paragraph, 1 means - a whole tutorial). Add them up and sort. +5. **Rank.** Score each gap from 1 to 5 on three things: how many users it + affects, how bad it is to be without it, and how much evidence you have + that people want it. Add those three up; that is the impact score, and + you sort by it. Separately, score how easy it is to write (5 means a + paragraph, 1 means a whole tutorial) and show it in its own column. Use + ease only to break ties. Folding it into the sum lets a trivial gap + nobody asked for outrank a genuinely undocumented feature. ## What to report -For each gap: its label, its score, what is missing, your evidence, where it -should go (which file, and where in the sidebar or which heading), and a -rough outline. Put the top ten in the PR and collapse the rest. +For each gap: its label, its impact score and ease score, what is missing, +your evidence, where it should go (which file, and where in the sidebar or +which heading), and a rough outline. Put the top ten in the PR description +and collapse the rest. + +The report is the deliverable. A normal run of this skill opens a PR with an +empty or near-empty diff and a long description. That is expected. ## What you may change @@ -48,5 +63,7 @@ Only one thing: add a single sentence linking to a page that clearly already covers the topic. Everything else is a suggestion. Thin generated adaptor pages are not gaps here; they are code-comment issues -for `OpenFn/adaptors`. If a feature is behind a feature flag, do not propose -documenting it. Ask the product team instead. +for `OpenFn/adaptors`. If a feature is behind a feature flag, or a command is +deliberately hidden in code (for example a CLI command with `describe: +false`), it is not a gap. Do not propose documenting it. Note it and ask the +product team if it looks like it should be public. From 57cb4d707b978eba96a00876cba75a78efcf510c Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 4 Sep 2026 14:46:13 +0000 Subject: [PATCH 13/13] Replace PR analysis with release review Defaults to every release in the last month across the product repos and reads changelogs before diffs. A single PR remains an optional narrower input. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CntaKNYyBkumpWvm5mTJa7 --- .agents/skills/pr-analysis.md | 88 -------------------------------- .agents/skills/release-review.md | 80 +++++++++++++++++++++++++++++ AGENTS.md | 9 ++-- 3 files changed, 85 insertions(+), 92 deletions(-) delete mode 100644 .agents/skills/pr-analysis.md create mode 100644 .agents/skills/release-review.md diff --git a/.agents/skills/pr-analysis.md b/.agents/skills/pr-analysis.md deleted file mode 100644 index 226d45a70141..000000000000 --- a/.agents/skills/pr-analysis.md +++ /dev/null @@ -1,88 +0,0 @@ -# PR analysis - -Read a pull request in one of the product repos and make sure the docs still -describe what the product does after it merges. Run this when someone gives -you a PR, or a range of commits or a release tag, from `OpenFn/lightning`, -`OpenFn/kit`, or `OpenFn/adaptors`. - -This is the inverse of the accuracy check. That skill starts from a docs page -and looks for the code. This one starts from a code change and looks for the -docs. - -## Two repos are involved - -The PR lives in a product repo. The docs live here. You need both checked -out. - -- **If you are running in the docs repo** and someone gives you a PR link, - clone the product repo into a scratch directory outside this one, then - fetch the PR: `git fetch origin pull/<number>/head:pr-<number>`. Diff it - against the base branch. Do not modify the product repo. -- **If you are running inside Lightning, kit, or adaptors** (for example, - someone on a product PR asks "does this need a docs change?"), clone - `OpenFn/docs` into a scratch directory and follow the same steps. Any docs - changes go in a branch and PR on the docs repo, never in the product PR. - -Either way, name the product repo, PR number, and head commit at the top of -your report so a reader knows exactly what you looked at. - -## Steps - -1. **Read the PR.** Start with the title, description, and any linked issue, - then the diff. Write down, in plain terms, what changed for a user: a new - feature, a renamed button, a new CLI flag, a changed default, a removed - option, a different error message, a new config value. Ignore anything a - user would never see: refactors, tests, dependency bumps, internal renames. - If nothing user-facing changed, say so and stop. - -2. **Find the docs that talk about it.** For each user-facing change, search - the docs for the feature, the old and new names, the flag, the setting, and - any screenshots of that screen. Search `docs/`, `articles/`, and - `adaptors/*.md`. Note every page and line that mentions it. - -3. **Decide what each page needs.** Go through the mentions and sort them: - - **Now wrong.** The docs describe the old behaviour. This is a fix if the - new behaviour is clear from the diff and slots into the existing - sentence (a renamed flag, a changed default). Otherwise it is a - suggestion with proposed wording. - - **Now incomplete.** The page is still right but does not mention the new - thing. Suggest where the new paragraph or table row should go and draft - it. - - **Nothing in the docs.** A new feature with no home yet. Suggest which - page or section it belongs in and give a short outline. Do not write the - page unless asked. - - **Screenshot affected.** The change alters a screen that appears in an - image. List the image so it goes on the retake list. Never retake it. - -4. **Check the PR's own docs claims.** If the PR description says "docs - updated" or links a docs PR, check that what it says matches what the diff - does. If the PR touches user-facing text in the app (button labels, error - messages, help text), search the docs for the old text. - -5. **Be careful about timing.** If the PR is not merged yet, say so at the top - of your report, and do not change any docs page. Docs should describe what - is released. Write everything up as suggestions and note which release the - change is expected in. - -## Adaptor PRs - -Changes to function signatures and descriptions in `OpenFn/adaptors` flow -into the docs automatically through the generated reference pages. You do -not need to do anything for those. Look only at the hand-written overview -page (`adaptors/<name>.md`) and at any tutorial or guide that uses the -changed function. If the PR changes a function's behaviour but not its code -comment, that is an upstream issue for the adaptors repo, not a docs fix. - -## What to report - -Start with one paragraph: what the PR does for users, whether it is merged, -and how many docs pages are affected. Then the findings in the standard -format, grouped by page. Finish with the images that need retaking and any -new pages that are needed. - -If you were asked to make the changes and the PR is merged, apply the fixes -in the docs repo, run Prettier and `yarn build`, and open a docs PR that links -back to the product PR. Everything else goes in that PR's description as -suggestions. If you were invoked from the product PR, leave one comment there -linking to the docs PR or summarising the findings, so the product reviewer -can see the docs were considered. diff --git a/.agents/skills/release-review.md b/.agents/skills/release-review.md new file mode 100644 index 000000000000..5c6406f31f0f --- /dev/null +++ b/.agents/skills/release-review.md @@ -0,0 +1,80 @@ +# Release review + +Look at what the product shipped recently and make sure the docs caught up. +With no arguments, review every release in `OpenFn/lightning`, `OpenFn/kit`, +and `OpenFn/adaptors` from the last month. Someone can narrow it to one repo, +a date range, a release tag, or a single PR. + +This is the inverse of the accuracy check. That skill starts from a docs page +and looks for the code. This one starts from what changed in the code and +looks for the docs. + +## Two repos are involved + +The releases live in the product repos. The docs live here. You need both. + +- **Running in the docs repo** (the usual case): clone each product repo you + need into a scratch directory outside this one. Fetch tags. Do not modify + the product repos. +- **Running inside a product repo** (someone asks "did the docs keep up with + this release?"): clone `OpenFn/docs` into a scratch directory and do the + same work. Docs changes always go in a branch and PR on the docs repo. + +Say at the top of your report which repos, tags, and dates you covered. + +## Steps + +1. **Start from the changelogs, not the diffs.** Each repo keeps one: + Lightning has a single `CHANGELOG.md`; kit and adaptors have one per + package under `packages/<name>/CHANGELOG.md`. Read every entry released in + the period. These are already a curated list of user-facing changes, so + they are cheaper and more reliable than reading every PR. Skip the + Unreleased section; docs describe what has shipped. + +2. **Turn each entry into a plain statement of what changed for a user**: a + new feature, a renamed button, a new CLI flag, a changed default, a removed + option, a new setting. Drop entries that are internal (refactors, + dependency bumps, test changes). If an entry is too vague to act on, open + the PR it links to and read the diff. Only then. + +3. **Find the docs that talk about it.** For each change, search `docs/`, + `articles/`, and `adaptors/*.md` for the feature, the old and new names, + the flag, or the setting. Search for things the way a user would type + them ("openfn pull --beta", not "beta"). Note every page and line. + +4. **Decide what each page needs.** + - **Now wrong.** The docs describe the old behaviour. Fix it if the new + behaviour is clear and slots into the existing sentence. Otherwise + suggest wording. + - **Now incomplete.** The page is still right but does not mention the new + thing. Suggest where the paragraph or table row goes and draft it. + - **Nothing in the docs.** A new feature with no home. Suggest a page or + section and a short outline. Do not write the page unless asked. + - **Screenshot affected.** The change alters a screen that appears in an + image. List the image for the retake list. Never retake it. + + A feature behind a feature flag, or a command hidden in code, is not a + docs gap. Note it and ask the product team if it looks like it should be + public. + +## Adaptors are different + +Changes to adaptor functions flow into the docs automatically through the +generated reference pages, so you do not need to chase those. For adaptors, +ask only two questions: is there a new adaptor with no overview page in +`adaptors/`, and did a change break something a tutorial or guide relies on. + +## What to report + +Start with one paragraph: the repos and period covered, how many releases, +how many user-facing changes, and how many docs pages are affected. Then the +findings in the standard format, grouped by page. Finish with images that +need retaking and new pages that are needed. + +Apply the fixes, run Prettier and `yarn build`, and open a docs PR that links +to the releases it covers. Suggestions and questions go in the PR +description. If the period had no user-facing changes, say so and do not open +a PR. + +This skill suits a monthly schedule with default arguments, plus a manual run +after any large release. diff --git a/AGENTS.md b/AGENTS.md index cfa22812bc7c..4227d03cd0ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,10 +51,11 @@ repo. Never change them. If the user asks for one skill only, run that one and still finish with a PR. -One skill runs from the other direction. **PR analysis** starts from a pull -request in a product repo and finds the docs pages that need to change -because of it. Run it when someone hands you a product PR, a commit range, or -a release tag. +One skill runs from the other direction. **Release review** starts from what +the product shipped, by default every release in the last month, and finds +the docs pages that need to change because of it. Run it on a monthly +schedule, or by hand after a big release, or narrowed to one PR if someone +asks. ## Pick one section