From 081fdc60b684478c5921e412b8b3fd47d5da6ba0 Mon Sep 17 00:00:00 2001 From: stepskop Date: Tue, 4 Aug 2026 14:38:15 +0200 Subject: [PATCH 1/6] feat: add copy proofread review workflow Reviews the public-facing copy changed in a PR against the style rules in an agent skill, checked out at runtime from apify/agent-skills-internal so callers do not vendor a copy that drifts. Callers pass the paths to review and, optionally, repo-specific rules for telling copy apart from code. --- .github/workflows/copy_proofread_review.yaml | 186 +++++++++++++++++++ README.md | 34 ++++ 2 files changed, 220 insertions(+) create mode 100644 .github/workflows/copy_proofread_review.yaml diff --git a/.github/workflows/copy_proofread_review.yaml b/.github/workflows/copy_proofread_review.yaml new file mode 100644 index 0000000..14b6423 --- /dev/null +++ b/.github/workflows/copy_proofread_review.yaml @@ -0,0 +1,186 @@ +name: copy proofread review + +# Proofreads the public-facing copy changed in a pull request against Apify's style guide and posts +# inline suggestions. The style rules come from the `apify-proofreader` skill in +# apify/agent-skills-internal, checked out at run time, so the review always applies the current rules +# instead of a copy vendored into the caller. +# +# The caller owns the trigger. The usual setup is `pull_request: types: [labeled]` plus an `if:` on the +# label name, which makes the review an explicit, re-requestable action rather than something that runs +# on every push. See the README for a full caller example. + +on: + workflow_call: + inputs: + paths: + description: Newline-separated list of paths (relative to the repo root) whose copy should be reviewed. Everything else in the diff is ignored. + required: true + type: string + repoInstructions: + description: >- + Repo-specific rules for telling copy apart from code, appended to the generic ones. Use it for + conventions the model cannot infer, e.g. which argument of a helper holds the human-readable message. + required: false + default: '' + type: string + + secrets: + anthropicApiKey: + description: Anthropic API key used by the review agent. + required: true + agentSkillsToken: + description: GitHub token with read access to apify/agent-skills-internal. + required: true + +# One run per PR; a second label-add while a run is in flight cancels the stale one. +concurrency: + group: copy-proofread-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + proofread: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: write # remove the trigger label when done + pull-requests: write # post the review + inline comments + id-token: write + steps: + - name: Check the secrets are set + # `secrets: inherit` does not bind these — the names below are this workflow's, not the caller's — + # so without this the run fails later as an opaque 401 from Anthropic or a checkout auth error. + env: + ANTHROPIC_API_KEY: ${{ secrets.anthropicApiKey }} + AGENT_SKILLS_TOKEN: ${{ secrets.agentSkillsToken }} + run: | + set -euo pipefail + missing=() + [[ -n "${ANTHROPIC_API_KEY:-}" ]] || missing+=(anthropicApiKey) + [[ -n "${AGENT_SKILLS_TOKEN:-}" ]] || missing+=(agentSkillsToken) + if (( ${#missing[@]} )); then + echo "::error::Missing secrets: ${missing[*]}. Pass them explicitly in the caller's \`secrets:\` block — \`secrets: inherit\` does not work here." + exit 1 + fi + + - name: Checkout repository + uses: actions/checkout@v6 + with: + # The diff comes from `gh pr diff` (API-backed) and files are read from the working tree. + fetch-depth: 1 + + - name: Check out the apify-proofreader skill + uses: actions/checkout@v6 + with: + repository: apify/agent-skills-internal + token: ${{ secrets.agentSkillsToken }} + path: .agent-skills + sparse-checkout: skills/apify-proofreader + fetch-depth: 1 + + - name: Install the skill into .claude/skills + # Claude Code auto-loads skills from `.claude/skills/` in the working directory, so the checkout + # above is moved into place rather than referenced where it landed. + run: | + set -euo pipefail + mkdir -p .claude/skills + cp -R .agent-skills/skills/apify-proofreader .claude/skills/apify-proofreader + rm -rf .agent-skills + + - name: Proofread public-facing copy + uses: anthropics/claude-code-action@v1 + env: + GH_TOKEN: ${{ github.token }} + with: + anthropic_api_key: ${{ secrets.anthropicApiKey }} + github_token: ${{ github.token }} + show_full_output: true + prompt: | + You are running inside a GitHub Actions workflow on pull request #${{ github.event.pull_request.number }} + in repository ${{ github.repository }}. Your + single job: proofread the **public-facing copy** changed in this PR against Apify's style guide and + post inline suggestions. You do NOT approve, request changes, or comment on anything else. + + ## Authority: the apify-proofreader skill + Use the `apify-proofreader` skill (checked out to .claude/skills/apify-proofreader/SKILL.md by a + previous CI step) as the ONLY source of style rules. Read that SKILL.md first and apply exactly those + rules — sentence case, "Actor" capitalization, US spelling, no em/en dashes, AI-fluff removal, etc. Do + not invent rules beyond it. If the file is missing, post a single PR comment saying the skill is not + installed and stop. + + ## Scope — ONLY these paths, and ONLY the copy within them + Consider ONLY changed hunks (lines beginning `+`) under: + ${{ inputs.paths }} + Ignore every other file in the diff. + + ## Distinguish copy from code — comment ONLY on copy + You MUST tell code apart from human-facing copy and only flag the copy. Copy is natural language a + user reads. Everything else is code and must be reproduced byte-for-byte: identifiers, object keys, + placeholders and format syntax (`{count}`, `{actorCount, plural, ...}`, `{type, select, ...}`), markup + and JSX tags, imports, component and prop names, `className`, style objects, URLs, error codes, and + HTTP status codes. Proofread only the natural-language words around them. + If you are unsure whether something is copy or code, do NOT comment on it. + ${{ inputs.repoInstructions }} + + ## How to post feedback — inline suggestions on changed lines only + For each real style violation on a `+` (added/changed) line: + - Use `mcp__github_inline_comment__create_inline_comment` to attach a comment to that exact line. + - Prefer a ready-to-apply suggestion. The comment body must be one short sentence naming the rule, + followed by a suggestion block that reproduces the WHOLE line with only the copy corrected and + all surrounding code/keys/placeholders/tags intact: + + + ```suggestion + + ``` + - If the fix is ambiguous (e.g. an ambiguous numeric date/number per the skill, an unlisted brand + name, or copy whose intended meaning is unclear), leave a comment WITHOUT a suggestion block and + ask the author to revise. NEVER pack several issues into one prose paragraph — format it as one + short lead sentence, then a bulleted list with ONE issue per bullet, each quoting the offending + text first: + + + + - `""` — (Rule N) + - `""` — (Rule N) + - Comment only on lines that are part of this PR's diff. One comment per issue; do not repeat the + same issue on many lines — cover the first and mention the pattern once. Post each finding + exactly once: if `create_inline_comment` returns an error, fix the arguments and retry, but never + post a second, reworded version of a comment that already landed. + + ## Finish + - After adding inline comments, post ONE top-level PR comment via `gh pr comment` listing the files + reviewed and the themes found (e.g. "3 sentence-case fixes, 1 em dash"). Keep it to a few bullets. + - Do NOT narrate your own method or scope in that comment. Never write sentences about what you + left untouched or how you distinguished copy from code (e.g. "I checked the string values only, + leaving object keys, ICU syntax, placeholders, and markup tags untouched") — that's plumbing, not + feedback. Report findings only. + - End the comment with a line crediting the rule source, exactly: + `Rules: [apify-proofreader](https://github.com/apify/agent-skills-internal/blob/main/skills/apify-proofreader/SKILL.md)` + - If you found NO violations, post a single top-level comment: "✅ Copy proofread — no style + issues found in the changed copy." plus that same `Rules:` line. Do not add inline comments in + that case. + - NEVER approve or request changes. You are only leaving comments. + claude_args: | + --max-turns 25 + --allowedTools "Read,Glob,Grep,Skill,mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr comment:*),Bash(cat:*),Bash(ls:*),Bash(grep:*),Bash(find:*)" + + - name: Remove trigger label + # Removes whichever label triggered the caller, so the label reflects "not currently queued" and + # re-adding it re-runs the review — the deliberate re-request action. Runs regardless of the review + # outcome, and is skipped when the caller was triggered by something other than a label. + if: always() && github.event.label.name != '' + uses: actions/github-script@v9 + with: + github-token: ${{ github.token }} + script: | + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + name: context.payload.label.name, + }); + } catch (error) { + if (error.status !== 404) throw error; + } diff --git a/README.md b/README.md index 51166fd..e7f721e 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,37 @@ jobs: slackChannelId: actorOverride: ${{ needs.get_values.outputs.commit_author }} ``` + +Proofread the public-facing copy changed in a pull request. The rules come from a skill in +[`apify/agent-skills-internal`](https://github.com/apify/agent-skills-internal), so callers do not vendor a +copy of them. The caller owns the trigger; label-driven keeps the review an explicit, re-requestable +action instead of something that runs on every push. + +> NOTE: `agentSkillsToken` needs read access to `apify/agent-skills-internal`. `APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN` works. + +```yml +name: copy proofread review + +on: + pull_request: + types: [labeled] + +jobs: + proofread: + if: github.event.label.name == 'copy-review' + uses: apify/workflows/.github/workflows/copy_proofread_review.yaml@main + secrets: + anthropicApiKey: ${{ secrets.ANTHROPIC_API_KEY }} + agentSkillsToken: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} + with: + paths: | + - `src/packages/intl/src/en/` + - `src/packages/errors/src/errors/` + # Optional — only for conventions the model cannot infer from the code itself. + repoInstructions: | + - errors `*.ts`: copy is ONLY the 2nd argument of `newMeteorishError(code, message, status)`. + The kebab-case error code and the numeric HTTP status are code. +``` + +Both secrets must be passed explicitly. `secrets: inherit` does not work here, because the names the workflow +declares are its own, not the caller's. From 645d4c7a12b337b40e3b1baebd28eba6d0c4e496 Mon Sep 17 00:00:00 2001 From: stepskop Date: Wed, 5 Aug 2026 12:38:48 +0200 Subject: [PATCH 2/6] fix: use the skill indirectly --- .github/workflows/copy_proofread_review.yaml | 38 +++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/.github/workflows/copy_proofread_review.yaml b/.github/workflows/copy_proofread_review.yaml index 14b6423..c0cde4b 100644 --- a/.github/workflows/copy_proofread_review.yaml +++ b/.github/workflows/copy_proofread_review.yaml @@ -1,13 +1,9 @@ name: copy proofread review # Proofreads the public-facing copy changed in a pull request against Apify's style guide and posts -# inline suggestions. The style rules come from the `apify-proofreader` skill in -# apify/agent-skills-internal, checked out at run time, so the review always applies the current rules -# instead of a copy vendored into the caller. +# inline suggestions. Rules come from the `apify-proofreader` skill in apify/agent-skills-internal. # -# The caller owns the trigger. The usual setup is `pull_request: types: [labeled]` plus an `if:` on the -# label name, which makes the review an explicit, re-requestable action rather than something that runs -# on every push. See the README for a full caller example. +# The caller owns the trigger. See the README for a full caller example. on: workflow_call: @@ -48,8 +44,7 @@ jobs: id-token: write steps: - name: Check the secrets are set - # `secrets: inherit` does not bind these — the names below are this workflow's, not the caller's — - # so without this the run fails later as an opaque 401 from Anthropic or a checkout auth error. + # `secrets: inherit` does not bind these — pass them explicitly in the caller. env: ANTHROPIC_API_KEY: ${{ secrets.anthropicApiKey }} AGENT_SKILLS_TOKEN: ${{ secrets.agentSkillsToken }} @@ -70,6 +65,8 @@ jobs: fetch-depth: 1 - name: Check out the apify-proofreader skill + # Checked out here rather than under `.claude/` — claude-code-action wipes and restores `.claude/` + # from the PR base branch before running Claude. uses: actions/checkout@v6 with: repository: apify/agent-skills-internal @@ -78,15 +75,6 @@ jobs: sparse-checkout: skills/apify-proofreader fetch-depth: 1 - - name: Install the skill into .claude/skills - # Claude Code auto-loads skills from `.claude/skills/` in the working directory, so the checkout - # above is moved into place rather than referenced where it landed. - run: | - set -euo pipefail - mkdir -p .claude/skills - cp -R .agent-skills/skills/apify-proofreader .claude/skills/apify-proofreader - rm -rf .agent-skills - - name: Proofread public-facing copy uses: anthropics/claude-code-action@v1 env: @@ -102,11 +90,11 @@ jobs: post inline suggestions. You do NOT approve, request changes, or comment on anything else. ## Authority: the apify-proofreader skill - Use the `apify-proofreader` skill (checked out to .claude/skills/apify-proofreader/SKILL.md by a - previous CI step) as the ONLY source of style rules. Read that SKILL.md first and apply exactly those - rules — sentence case, "Actor" capitalization, US spelling, no em/en dashes, AI-fluff removal, etc. Do - not invent rules beyond it. If the file is missing, post a single PR comment saying the skill is not - installed and stop. + Use the `apify-proofreader` skill (checked out to .agent-skills/skills/apify-proofreader/SKILL.md by + a previous CI step) as the ONLY source of style rules. Read that SKILL.md first and apply exactly + those rules — sentence case, "Actor" capitalization, US spelling, no em/en dashes, AI-fluff removal, + etc. Do not invent rules beyond it. If the file is missing, post a single PR comment saying the + skill is not installed and stop. ## Scope — ONLY these paths, and ONLY the copy within them Consider ONLY changed hunks (lines beginning `+`) under: @@ -163,12 +151,10 @@ jobs: - NEVER approve or request changes. You are only leaving comments. claude_args: | --max-turns 25 - --allowedTools "Read,Glob,Grep,Skill,mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr comment:*),Bash(cat:*),Bash(ls:*),Bash(grep:*),Bash(find:*)" + --allowedTools "Read,Glob,Grep,mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr comment:*),Bash(cat:*),Bash(ls:*),Bash(grep:*),Bash(find:*)" - name: Remove trigger label - # Removes whichever label triggered the caller, so the label reflects "not currently queued" and - # re-adding it re-runs the review — the deliberate re-request action. Runs regardless of the review - # outcome, and is skipped when the caller was triggered by something other than a label. + # Re-adding it re-runs the review. if: always() && github.event.label.name != '' uses: actions/github-script@v9 with: From 54f712c03d213350a45414f6575753d3bbfc715e Mon Sep 17 00:00:00 2001 From: stepskop Date: Wed, 5 Aug 2026 14:00:46 +0200 Subject: [PATCH 3/6] chore: remove readme --- README.md | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/README.md b/README.md index e7f721e..51166fd 100644 --- a/README.md +++ b/README.md @@ -46,37 +46,3 @@ jobs: slackChannelId: actorOverride: ${{ needs.get_values.outputs.commit_author }} ``` - -Proofread the public-facing copy changed in a pull request. The rules come from a skill in -[`apify/agent-skills-internal`](https://github.com/apify/agent-skills-internal), so callers do not vendor a -copy of them. The caller owns the trigger; label-driven keeps the review an explicit, re-requestable -action instead of something that runs on every push. - -> NOTE: `agentSkillsToken` needs read access to `apify/agent-skills-internal`. `APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN` works. - -```yml -name: copy proofread review - -on: - pull_request: - types: [labeled] - -jobs: - proofread: - if: github.event.label.name == 'copy-review' - uses: apify/workflows/.github/workflows/copy_proofread_review.yaml@main - secrets: - anthropicApiKey: ${{ secrets.ANTHROPIC_API_KEY }} - agentSkillsToken: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} - with: - paths: | - - `src/packages/intl/src/en/` - - `src/packages/errors/src/errors/` - # Optional — only for conventions the model cannot infer from the code itself. - repoInstructions: | - - errors `*.ts`: copy is ONLY the 2nd argument of `newMeteorishError(code, message, status)`. - The kebab-case error code and the numeric HTTP status are code. -``` - -Both secrets must be passed explicitly. `secrets: inherit` does not work here, because the names the workflow -declares are its own, not the caller's. From 67b10b577db8659dd3b56fc43f5fde07f7616dcd Mon Sep 17 00:00:00 2001 From: Skopek Date: Wed, 5 Aug 2026 14:01:37 +0200 Subject: [PATCH 4/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/copy_proofread_review.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/copy_proofread_review.yaml b/.github/workflows/copy_proofread_review.yaml index c0cde4b..e6c62c1 100644 --- a/.github/workflows/copy_proofread_review.yaml +++ b/.github/workflows/copy_proofread_review.yaml @@ -41,7 +41,6 @@ jobs: contents: read issues: write # remove the trigger label when done pull-requests: write # post the review + inline comments - id-token: write steps: - name: Check the secrets are set # `secrets: inherit` does not bind these — pass them explicitly in the caller. From 08d23240dfcdd82fa387bfb61eeaa8595e7cc486 Mon Sep 17 00:00:00 2001 From: Skopek Date: Wed, 5 Aug 2026 14:02:14 +0200 Subject: [PATCH 5/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/copy_proofread_review.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/copy_proofread_review.yaml b/.github/workflows/copy_proofread_review.yaml index e6c62c1..e813fe7 100644 --- a/.github/workflows/copy_proofread_review.yaml +++ b/.github/workflows/copy_proofread_review.yaml @@ -30,7 +30,7 @@ on: # One run per PR; a second label-add while a run is in flight cancels the stale one. concurrency: - group: copy-proofread-review-${{ github.event.pull_request.number }} + group: copy-proofread-review-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true jobs: From 779c4bebe71a445fc7db20b557a484bff5d909f8 Mon Sep 17 00:00:00 2001 From: stepskop Date: Wed, 5 Aug 2026 14:12:04 +0200 Subject: [PATCH 6/6] chore: better naming --- .github/workflows/copy_proofread_review.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/copy_proofread_review.yaml b/.github/workflows/copy_proofread_review.yaml index e813fe7..d343ea1 100644 --- a/.github/workflows/copy_proofread_review.yaml +++ b/.github/workflows/copy_proofread_review.yaml @@ -24,7 +24,7 @@ on: anthropicApiKey: description: Anthropic API key used by the review agent. required: true - agentSkillsToken: + agentSkillsRepoGithubToken: description: GitHub token with read access to apify/agent-skills-internal. required: true @@ -46,12 +46,12 @@ jobs: # `secrets: inherit` does not bind these — pass them explicitly in the caller. env: ANTHROPIC_API_KEY: ${{ secrets.anthropicApiKey }} - AGENT_SKILLS_TOKEN: ${{ secrets.agentSkillsToken }} + AGENT_SKILLS_TOKEN: ${{ secrets.agentSkillsRepoGithubToken }} run: | set -euo pipefail missing=() [[ -n "${ANTHROPIC_API_KEY:-}" ]] || missing+=(anthropicApiKey) - [[ -n "${AGENT_SKILLS_TOKEN:-}" ]] || missing+=(agentSkillsToken) + [[ -n "${AGENT_SKILLS_TOKEN:-}" ]] || missing+=(agentSkillsRepoGithubToken) if (( ${#missing[@]} )); then echo "::error::Missing secrets: ${missing[*]}. Pass them explicitly in the caller's \`secrets:\` block — \`secrets: inherit\` does not work here." exit 1 @@ -69,7 +69,7 @@ jobs: uses: actions/checkout@v6 with: repository: apify/agent-skills-internal - token: ${{ secrets.agentSkillsToken }} + token: ${{ secrets.agentSkillsRepoGithubToken }} path: .agent-skills sparse-checkout: skills/apify-proofreader fetch-depth: 1