Skip to content

Commit c5469c4

Browse files
committed
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.
1 parent 534e4c1 commit c5469c4

2 files changed

Lines changed: 201 additions & 0 deletions

File tree

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
name: copy proofread review
2+
3+
# Proofreads the public-facing copy changed in a pull request against Apify's style guide and posts
4+
# inline suggestions. The style rules come from the `apify-proofreader` skill in
5+
# apify/agent-skills-internal, checked out at run time, so the review always applies the current rules
6+
# instead of a copy vendored into the caller.
7+
#
8+
# The caller owns the trigger. The usual setup is `pull_request: types: [labeled]` plus an `if:` on the
9+
# label name, which makes the review an explicit, re-requestable action rather than something that runs
10+
# on every push. See the README for a full caller example.
11+
12+
on:
13+
workflow_call:
14+
inputs:
15+
paths:
16+
description: Newline-separated list of paths (relative to the repo root) whose copy should be reviewed. Everything else in the diff is ignored.
17+
required: true
18+
type: string
19+
repoInstructions:
20+
description: >-
21+
Repo-specific rules for telling copy apart from code, appended to the generic ones. Use it for
22+
conventions the model cannot infer, e.g. which argument of a helper holds the human-readable message.
23+
required: false
24+
default: ''
25+
type: string
26+
27+
secrets:
28+
anthropicApiKey:
29+
description: Anthropic API key used by the review agent.
30+
required: true
31+
agentSkillsToken:
32+
description: GitHub token with read access to apify/agent-skills-internal.
33+
required: true
34+
35+
# One run per PR; a second label-add while a run is in flight cancels the stale one.
36+
concurrency:
37+
group: copy-proofread-review-${{ github.event.pull_request.number }}
38+
cancel-in-progress: true
39+
40+
jobs:
41+
proofread:
42+
runs-on: ubuntu-latest
43+
timeout-minutes: 10
44+
permissions:
45+
contents: read
46+
issues: write # remove the trigger label when done
47+
pull-requests: write # post the review + inline comments
48+
id-token: write
49+
steps:
50+
- name: Checkout repository
51+
uses: actions/checkout@v6
52+
with:
53+
# The diff comes from `gh pr diff` (API-backed) and files are read from the working tree.
54+
fetch-depth: 1
55+
56+
- name: Check out the apify-proofreader skill
57+
uses: actions/checkout@v6
58+
with:
59+
repository: apify/agent-skills-internal
60+
token: ${{ secrets.agentSkillsToken }}
61+
path: .agent-skills
62+
sparse-checkout: skills/apify-proofreader
63+
fetch-depth: 1
64+
65+
- name: Install the skill into .claude/skills
66+
# Claude Code auto-loads skills from `.claude/skills/` in the working directory, so the checkout
67+
# above is moved into place rather than referenced where it landed.
68+
run: |
69+
set -euo pipefail
70+
mkdir -p .claude/skills
71+
cp -R .agent-skills/skills/apify-proofreader .claude/skills/apify-proofreader
72+
rm -rf .agent-skills
73+
74+
- name: Proofread public-facing copy
75+
uses: anthropics/claude-code-action@v1
76+
env:
77+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78+
with:
79+
anthropic_api_key: ${{ secrets.anthropicApiKey }}
80+
github_token: ${{ secrets.GITHUB_TOKEN }}
81+
show_full_output: true
82+
prompt: |
83+
You are running inside a GitHub Actions workflow on pull request #${{ github.event.pull_request.number }}
84+
in repository ${{ github.repository }}. Your
85+
single job: proofread the **public-facing copy** changed in this PR against Apify's style guide and
86+
post inline suggestions. You do NOT approve, request changes, or comment on anything else.
87+
88+
## Authority: the apify-proofreader skill
89+
Use the `apify-proofreader` skill (checked out to .claude/skills/apify-proofreader/SKILL.md by a
90+
previous CI step) as the ONLY source of style rules. Read that SKILL.md first and apply exactly those
91+
rules — sentence case, "Actor" capitalization, US spelling, no em/en dashes, AI-fluff removal, etc. Do
92+
not invent rules beyond it. If the file is missing, post a single PR comment saying the skill is not
93+
installed and stop.
94+
95+
## Scope — ONLY these paths, and ONLY the copy within them
96+
Consider ONLY changed hunks (lines beginning `+`) under:
97+
${{ inputs.paths }}
98+
Ignore every other file in the diff.
99+
100+
## Distinguish copy from code — comment ONLY on copy
101+
You MUST tell code apart from human-facing copy and only flag the copy. Copy is natural language a
102+
user reads. Everything else is code and must be reproduced byte-for-byte: identifiers, object keys,
103+
placeholders and format syntax (`{count}`, `{actorCount, plural, ...}`, `{type, select, ...}`), markup
104+
and JSX tags, imports, component and prop names, `className`, style objects, URLs, error codes, and
105+
HTTP status codes. Proofread only the natural-language words around them.
106+
If you are unsure whether something is copy or code, do NOT comment on it.
107+
${{ inputs.repoInstructions }}
108+
109+
## How to post feedback — inline suggestions on changed lines only
110+
For each real style violation on a `+` (added/changed) line:
111+
- Use `mcp__github_inline_comment__create_inline_comment` to attach a comment to that exact line.
112+
- Prefer a ready-to-apply suggestion. The comment body must be one short sentence naming the rule,
113+
followed by a suggestion block that reproduces the WHOLE line with only the copy corrected and
114+
all surrounding code/keys/placeholders/tags intact:
115+
116+
<the style rule in one sentence>
117+
```suggestion
118+
<the full corrected line, code untouched>
119+
```
120+
- If the fix is ambiguous (e.g. an ambiguous numeric date/number per the skill, an unlisted brand
121+
name, or copy whose intended meaning is unclear), leave a comment WITHOUT a suggestion block and
122+
ask the author to revise. NEVER pack several issues into one prose paragraph — format it as one
123+
short lead sentence, then a bulleted list with ONE issue per bullet, each quoting the offending
124+
text first:
125+
126+
<one sentence: what you need from the author and why there's no suggestion>
127+
128+
- `"<offending text>"` — <the problem and the fix, one clause> (Rule N)
129+
- `"<offending text>"` — <the problem and the fix, one clause> (Rule N)
130+
- Comment only on lines that are part of this PR's diff. One comment per issue; do not repeat the
131+
same issue on many lines — cover the first and mention the pattern once. Post each finding
132+
exactly once: if `create_inline_comment` returns an error, fix the arguments and retry, but never
133+
post a second, reworded version of a comment that already landed.
134+
135+
## Finish
136+
- After adding inline comments, post ONE top-level PR comment via `gh pr comment` listing the files
137+
reviewed and the themes found (e.g. "3 sentence-case fixes, 1 em dash"). Keep it to a few bullets.
138+
- Do NOT narrate your own method or scope in that comment. Never write sentences about what you
139+
left untouched or how you distinguished copy from code (e.g. "I checked the string values only,
140+
leaving object keys, ICU syntax, placeholders, and markup tags untouched") — that's plumbing, not
141+
feedback. Report findings only.
142+
- End the comment with a line crediting the rule source, exactly:
143+
`Rules: [apify-proofreader](https://github.com/apify/agent-skills-internal/blob/main/skills/apify-proofreader/SKILL.md)`
144+
- If you found NO violations, post a single top-level comment: "✅ Copy proofread — no style
145+
issues found in the changed copy." plus that same `Rules:` line. Do not add inline comments in
146+
that case.
147+
- NEVER approve or request changes. You are only leaving comments.
148+
claude_args: |
149+
--max-turns 25
150+
--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:*)"
151+
152+
- name: Remove trigger label
153+
# Removes whichever label triggered the caller, so the label reflects "not currently queued" and
154+
# re-adding it re-runs the review — the deliberate re-request action. Runs regardless of the review
155+
# outcome, and is skipped when the caller was triggered by something other than a label.
156+
if: always() && github.event.label.name != ''
157+
uses: actions/github-script@v9
158+
with:
159+
github-token: ${{ secrets.GITHUB_TOKEN }}
160+
script: |
161+
try {
162+
await github.rest.issues.removeLabel({
163+
owner: context.repo.owner,
164+
repo: context.repo.repo,
165+
issue_number: context.payload.pull_request.number,
166+
name: context.payload.label.name,
167+
});
168+
} catch (error) {
169+
if (error.status !== 404) throw error;
170+
}

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,34 @@ jobs:
4646
slackChannelId: <SLACK_CHANNEL_ID>
4747
actorOverride: ${{ needs.get_values.outputs.commit_author }}
4848
```
49+
50+
Proofread the public-facing copy changed in a pull request. The rules come from a skill in
51+
[`apify/agent-skills-internal`](https://github.com/apify/agent-skills-internal), so callers do not vendor a
52+
copy of them. The caller owns the trigger; label-driven keeps the review an explicit, re-requestable
53+
action instead of something that runs on every push.
54+
55+
> NOTE: `agentSkillsToken` needs read access to `apify/agent-skills-internal`. `APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN` works.
56+
57+
```yml
58+
name: copy proofread review
59+
60+
on:
61+
pull_request:
62+
types: [labeled]
63+
64+
jobs:
65+
proofread:
66+
if: github.event.label.name == 'copy-review'
67+
uses: apify/workflows/.github/workflows/copy_proofread_review.yaml@main
68+
secrets:
69+
anthropicApiKey: ${{ secrets.ANTHROPIC_API_KEY }}
70+
agentSkillsToken: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
71+
with:
72+
paths: |
73+
- `src/packages/intl/src/en/`
74+
- `src/packages/errors/src/errors/`
75+
# Optional — only for conventions the model cannot infer from the code itself.
76+
repoInstructions: |
77+
- errors `*.ts`: copy is ONLY the 2nd argument of `newMeteorishError(code, message, status)`.
78+
The kebab-case error code and the numeric HTTP status are code.
79+
```

0 commit comments

Comments
 (0)