Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions .github/workflows/copy_proofread_review.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
name: copy proofread review

# Proofreads the public-facing copy changed in a pull request against Apify's style guide and posts
# inline suggestions. Rules come from the `apify-proofreader` skill in apify/agent-skills-internal.
#
# The caller owns the trigger. 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
agentSkillsRepoGithubToken:
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 || github.run_id }}
cancel-in-progress: true
Comment thread
Copilot marked this conversation as resolved.

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
steps:
- name: Check the secrets are set
# `secrets: inherit` does not bind these — pass them explicitly in the caller.
env:
ANTHROPIC_API_KEY: ${{ secrets.anthropicApiKey }}
AGENT_SKILLS_TOKEN: ${{ secrets.agentSkillsRepoGithubToken }}
run: |
set -euo pipefail
missing=()
[[ -n "${ANTHROPIC_API_KEY:-}" ]] || missing+=(anthropicApiKey)
[[ -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
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
# 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
token: ${{ secrets.agentSkillsRepoGithubToken }}
path: .agent-skills
sparse-checkout: skills/apify-proofreader
fetch-depth: 1

- 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 .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:
${{ 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:

<the style rule in one sentence>
```suggestion
<the full corrected line, code untouched>
```
- 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:

<one sentence: what you need from the author and why there's no suggestion>

- `"<offending text>"` — <the problem and the fix, one clause> (Rule N)
- `"<offending text>"` — <the problem and the fix, one clause> (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,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
# Re-adding it re-runs the review.
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;
}
Loading