Skip to content

fix(rc-docs-sync): max-turns + safety-net marker + public/internal heuristic#394

Merged
enricobattocchi merged 3 commits into
mainfrom
rc-docs-sync-fix-marker-safetynet
Apr 29, 2026
Merged

fix(rc-docs-sync): max-turns + safety-net marker + public/internal heuristic#394
enricobattocchi merged 3 commits into
mainfrom
rc-docs-sync-fix-marker-safetynet

Conversation

@enricobattocchi
Copy link
Copy Markdown
Member

@enricobattocchi enricobattocchi commented Apr 28, 2026

What

Three related fixes/refinements after the first real workflow run on 27.6-RC1 (run, PR #393):

  1. Bumped --max-turns from 50 to 100 in the agent step.
  2. Added a marker safety-net step (if: always()) that posts a marker comment to the tracking issue if the agent didn't.
  3. Added public-vs-internal discrimination to the agent prompt + AGENT_MAP.md, so the agent stops documenting routes that were intended as internal admin plumbing.

Why each change

1. Max-turns 100

The first run successfully triaged + authored + opened PR #393, then hit --max-turns 50 before posting the run-summary comment to issue #390:

error: Claude Code returned an error result: Reached maximum number of turns (50)

50 turns is too tight once you count triage + multi-file authoring + git checkout -b/add/commit/push + gh pr create + gh pr edit --add-label + comment posting. 100 gives realistic headroom; the 30-minute job timeout is still the outer bound.

2. Marker safety-net

Without a marker comment on the tracking issue, the next scheduled run would re-process the same RC and create a duplicate PR. (Already happened once; mitigated by a manually-posted marker on #390.) New step runs if: always() after the agent step, only when the agent was actually invoked, and posts a minimal marker comment if one isn't already there. Skips no-op when the agent succeeded and posted its own.

3. Public-vs-internal discrimination

PR #393 documented two new REST endpoints (yoast/v1/ai_content_planner/get_suggestions and get_outline). The question came up: were those intended as public APIs, or as internal admin-UI plumbing? If the latter, documenting them turns them into public commitments the source team didn't intend.

This commit teaches the agent to discriminate at triage time, in priority order:

  1. @internal PHPDoc annotation — authoritative override. Source authors mark a route or class as internal:
    /**
     * @internal Used by the Yoast SEO admin UI; not part of the public REST API.
     */
  2. Permission-callback heuristicregister_rest_route(...) whose permission_callback is an admin capability check (current_user_can('manage_options'), etc.) without an unauthenticated branch defaults to internal.
  3. Path/class-name heuristic — registrations under **/admin/**, **/user-interface/**, or named *_Admin_* / *_Internal_* default to internal.
  4. When in doubt, skip — false positives in the public direction are higher cost than false negatives.

Items skipped under this rule are listed in a new "Internal surface skipped" section of the run-summary comment, so the decision is auditable per-RC.

AGENT_MAP.md also documents this convention, so source-repo authors can find it without reading the agent prompt.

Risk

Low. The new step in the workflow only writes to the tracking issue we own, only runs when the agent step ran, and skips when a marker already exists. --max-turns 100 is just headroom. The internal-discrimination heuristic only changes triage decisions — it errs toward NOT documenting, which is the safer direction if it misfires.

After merging

No re-dispatch needed against 27.6-RC1 — the manual marker on #390 already handles that. The next run (cron or manual against a new RC) gets all three improvements.

Two related fixes after the first real run on 27.6-RC1:

1. Bumped --max-turns from 50 to 100 in claude_args. The agent successfully
   triaged + authored + opened PR #393 but ran out of turns before posting
   its run-summary comment. 50 turns is too tight once authoring + git
   ops + gh pr create + comment posting are all counted; 100 gives realistic
   headroom and is still capped well below the 30-minute job timeout.

2. Added a new "Ensure marker comment exists (safety net)" step that runs
   `if: always()` after the agent step. It checks whether a
   `<!-- rc-docs-sync:v1 product=<slug> rc_tag=<tag> -->` marker comment
   already exists on the tracking issue for this (product, rc_tag) pair;
   if not, it posts one referencing the workflow run.

This guarantees state always advances even if the agent fails for any
reason, so the next scheduled run will not silently re-process the same
RC and create a duplicate PR — which is exactly what would have happened
without the manual marker that was just added to issue #390 by hand.

The agent's normal run-summary comment is still preferred when it gets
posted; the safety-net step skips when the agent has already posted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 28, 2026

Deploying yoast-developer with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4b9d1e6
Status: ✅  Deploy successful!
Preview URL: https://8fc9fedb.yoast-developer.pages.dev
Branch Preview URL: https://rc-docs-sync-fix-marker-safe.yoast-developer.pages.dev

View logs

Triggered by feedback on PR #393, where the agent documented two REST
endpoints (yoast/v1/ai_content_planner/get_suggestions and get_outline)
that may have been intended as internal admin-UI plumbing. The agent had
no signal distinguishing public from internal surface — anything that
looked like a public registration was fair game.

This commit teaches the agent to discriminate, and documents the
convention authors can use to override the heuristics.

agent prompt (.github/claude-agent/run.md): adds Step 1.6 — Public vs.
internal surface — between coverage-gap detection and authoring. The
agent now applies, in order:

  1. Authoritative override: `@internal` PHPDoc annotation on the
     registering method or class. If present, skip and log.
  2. Permission-callback heuristic: `register_rest_route` whose
     permission_callback is `current_user_can('manage_options')` (or
     similar admin capability check) without an unauthenticated
     branch is likely internal.
  3. Path/class-name heuristic: registrations under `**/admin/**`,
     `**/user-interface/**`, or named `*_Admin_*` / `*_Internal_*`
     default to internal.
  4. When in doubt, don't document — false positives in the public
     direction are higher cost than false negatives.

Items skipped under this rule appear in a new "Internal surface
skipped" section of the run-summary comment, so the decision is
visible per-RC and a maintainer can override in a follow-up.

AGENT_MAP.md: adds a "Public vs. internal surface" section
documenting both the heuristics (automatic) and the @internal override
(authoritative), so source-repo authors know how to mark a route as
internal without reading the agent prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@enricobattocchi enricobattocchi changed the title fix(rc-docs-sync): bump --max-turns to 100 and add marker safety-net step fix(rc-docs-sync): max-turns + safety-net marker + public/internal heuristic Apr 29, 2026
@enricobattocchi enricobattocchi merged commit ba5fa7a into main Apr 29, 2026
1 check passed
@enricobattocchi enricobattocchi deleted the rc-docs-sync-fix-marker-safetynet branch April 29, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant