Skip to content

feat(pr-pipeline): restore pr-refine as the refine stage for fix-docs - #9332

Merged
kyle-sexton merged 16 commits into
mainfrom
feat/pr-refine-restore
Oct 23, 2026
Merged

kyle-sexton merged 16 commits into
mainfrom
feat/pr-refine-restore

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Refs: #9306
Refs: #8890

Depends on melodic-software/azure-iac#80 (deploy first)

Summary

The owner restored Refine as its own stage, which tidies every PR, lane-App PRs included (Q38, Q42; D8 accepted the model spend at the existing cap of 2 per push). fix-docs moves out of pr-unblock into a restored pr-refine lane, dispatched by the existing pr-unblock-dispatch.yml. The broker change that admits a dispatched pr-refine.yml run is melodic-software/azure-iac#80. Until that is deployed, the broker refuses every pr-refine mint as untrusted-actor, so it must deploy before this merges.

Fix

  • .github/workflows/pr-refine.yml (new): workflow_dispatch only, with inputs pr-number, activity (choice: fix-docs) and dispatcher-run-id. Run-name pr-refine <activity> #<pr>, concurrency per activity and PR. It takes fix-docs-head, fix-docs, fix-docs-subscription and apply-docs-patch from pr-unblock.yml unchanged except lane: pr-refine; the write queue stays pr-pipeline-write-<pr>.
  • .github/workflows/pr-unblock.yml: drops the fix-docs option, its header text and those four jobs.
  • docs/conventions/pr-pipeline.yaml: adds lanes.pr-refine (stage refine, slots fix-docs, then apply-patch with needs: [fix-docs]). pr-unblock's apply-patch now needs only fix-ci. The cap stays at limits.fix-docs-per-run: 2, once per PR.
  • pr-run-activity-read.yml and pr-run-activity-write.yml: dispatched-workflow is pr-refine.yml when inputs.lane == 'pr-refine', else pr-unblock.yml.
  • .github/scripts/pr-unblock/fix-docs-dispatch.mjs:
    • Dispatches pr-refine.yml with check name pr-refine / fix-docs, the <lane> / <activity> name report-check-run/decide.mjs:68 posts.
    • "Already ran" accepts both pr-refine / fix-docs and pr-unblock / fix-docs: on the head's checks, in the 50-commit history query (two aliased checkRuns fields) and in the dispatch record. A head recorded under the old name is re-recorded under the new one.
    • The in-flight check matches the dispatched workflow's own run-name. The shared runTitle always produced pr-unblock …, so it would never have matched a pr-refine run.
    • Removes the person-only filter. It now excludes by numeric account id, read from GraphQL databaseId, and skips an author with no id:
      • Dependabot: 49699333
      • melodic-plugin-release: 337859524
      • melodic-standards-sync: 300666570
  • pr-unblock-dispatch.yml and dispatch.mjs: prose, the actions: write comment, and the new history-query variable.
  • .github/scripts/pr-merge/refire-refused.mjs is unchanged. It still leaves pr-refine.yml out: a pr-refine run is a workflow_dispatch on main, so an empty commit on the PR cannot make it pass. After a workflow-modified refusal, a listed person re-dispatches pr-refine (review finding, fixed in 38a39ad).
  • Collateral: lane-funding.json (the fix-docs rows name pr-refine.yml), docs/conventions/pr-pipeline/README.md and pr-run-activity.md (the trailer is now Lane-Activity: pr-refine/fix-docs), docs/conventions/loop-lane/posting-lanes.md (pr-refine rows; review finding, fixed in 808cdc5), and a fragment under .changes/pr-pipeline/.
  • plugins/harness-ops/skills/morning-brief/scripts/morning-brief.sh: lists pr-refine.yml among the default lane workflows again (removed in chore(pr-pipeline): drop pr-refine.yml leftovers from pr-merge refire and morning brief #9039), with a fragment under .changes/harness-ops/. morning-brief.test.sh: 450 cases pass.
  • Security (pr-unblock: derive fix-ci-subscription's start and the restart cap from trusted values #8890, widened to fix-docs): fix-docs-subscription started on fix-docs's lane-class and credential-present outputs, and the head-code run job can forge both.
  • Not changed, per the coordinator's update: babysit_risk.py HIGH_FLOOR.

Verification

  • Stray stash pop, cleaned up. While merging main for fix(pr-pipeline): start the address-feedback and review-dependency fallback calls from trusted values #9368, a failed git stash push was followed by a stash pop, which applied an older, unrelated local stash (a numpy bump).

    • I undid its changes before committing, and the stash itself is untouched.
    • None of its files are in this PR's diff: .changes/{animation,playwright}/bump-numpy-2-5-4.md, .github/requirements-ci-animation.txt and plugins/playwright/skills/demo-video/requirements.in are absent (checked with git diff origin/main...HEAD --name-only).
  • TDD. New and changed cases failed before the code change:

    • fix-docs-dispatch.test.mjs: workflow, check names, author ids, old-name history and record.
  • Suites, all passing:

    Suite Pass/fail
    node --test .github/actions/resolve-config/*.test.mjs 632/0
    .github/scripts/pr-unblock, pr-merge, apply-patch, request-lane-token, check-trusted-trigger 1208/0
    fix-docs-dispatch.test.mjs 15/0
  • actionlint on the six changed workflows: clean.

  • scripts/check-affected.sh --tests on the head after merging origin/main: 11 passed, 3 skipped, 0 failed. The first run caught scripts/read-runner-dispatcher-gate.test.sh, which pinned dispatched-workflow to pr-unblock.yml; it now asserts the lane expression.

  • Broker compatibility. The deployed broker's pinned resolver (azure-iac resolve-config.pin 39fed34) was run on this head's pr-pipeline.yaml and trust files.

    • pr-refine/fix-docs resolves to read (contents, pull-requests and issues read).
    • pr-refine/apply-patch resolves to mutate-branch (contents write).
    • Every remaining pr-unblock slot grants the same as on origin/main; the only difference is that fix-docs moved lanes.
    • No BROKER_PATHS file changed, so no broker pin PR is needed.
  • Account ids:

  • feat(pr-pipeline): restore Refine as its own stage that tidies every PR before review #9306 stays open after merge: its last criterion, one live apply-docs-patch commit on a canary PR, can only run once pr-refine.yml is on main, since a pr-refine run is dispatched from main's tip. Record that commit's SHA on the issue (feat(pr-pipeline): restore Refine as its own stage that tidies every PR before review #9306) when it lands, then close it.

  • Not yet run: the sandbox canaries in the plan. These are a manual dispatch on a person's PR, a lane-App PR picked up, the skips for Dependabot, release, draft and held PRs, and no re-dispatch where a pr-unblock / fix-docs run exists.

Related

  • melodic-software/azure-iac#80: broker admits pr-refine.yml as a dispatched workflow; merged and deployed.
  • Owner decisions Q38 and Q42 (2026-10-23).
  • Cost decision: Q50, cost accepted (owner, 2026-10-23).

🤖 Generated with Claude Code

kyle-sexton and others added 2 commits October 23, 2026 00:52
pr-refine.yml takes fix-docs, its subscription fallback and its push job
out of pr-unblock.yml, and pr-pipeline.yaml gains lanes.pr-refine (stage
refine: fix-docs, then apply-patch). pr-unblock-dispatch.yml dispatches
pr-refine.yml once per PR; a pr-unblock / fix-docs run counts as done. The
person-only filter goes: every PR is tidied except Dependabot's, the
release App's and the standards-sync App's, excluded by account id. Both
runners pick the dispatched workflow by lane, and refire-refused covers
dispatched pr-refine runs again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton kyle-sexton added the do-not-merge Hard merge gate: do not merge while applied. label Oct 23, 2026
@kyle-sexton kyle-sexton removed the do-not-merge Hard merge gate: do not merge while applied. label Oct 23, 2026
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 23, 2026 05:15
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T05:17:39.421979Z ad8fac8 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

claude-review reviewed f0f60705b12f5fe68965d09b5d325ad51434d8a7: 0 finding(s), 0 as inline comments.

Code review: #9332 restore pr-refine as the refine stage for fix-docs

Result: no blocking or advisory findings. Held for a person on cost (below).

I read all 23 files of the diff (.claude-lane/pr.diff) and the PR context. I read fix-docs-dispatch.mjs and the listWorkflowRuns and dispatchWorkflow code in dispatch.mjs at head to check how they connect. I ran no tests, because this lane has no shell. The pass counts in the PR body (632/0, 1208/0, 15/0 and others) are author-claimed and I did not confirm them.

What I checked

  • Lane move.
    • fix-docs leaves pr-unblock.yml and pr-unblock's apply-patch.needs. It lands in the new pr-refine.yml and in lanes.pr-refine in pr-pipeline.yaml.
    • lane-funding.json, both runner dispatched-workflow expressions, scripts/read-runner-dispatcher-gate.test.sh and the docs are updated to match.
    • The new pr-refine.yml has no report-escalation. The callers.test.mjs branch for a lane with no escalation job asserts that EMPTY_RUNS_APPLY is false, which is true for fix-docs.
  • pr-unblock: derive fix-ci-subscription's start and the restart cap from trusted values #8890 handoff.
    • fix-docs-handoff has only actions: read, no checkout and no secrets.
    • fix-docs-subscription starts only on the handoff's posted != 'true' and allowed == 'true', and it reads no run-job output. A new test pins the step text equal to fix-ci-handoff.
    • apply-docs-patch takes one leg's scan-* outputs and the head from fix-docs-head. The if: and the with: expressions match each other and match the test's runs() cases.
    • The SUBSCRIPTION_START_PENDING escape hatch is removed, so the subscription-start guard now covers every subscription leg.
  • Dispatcher.
    • The in-flight title now comes from the dispatched workflow (pr-refine fix-docs #N), and it matches the new run-name.
    • The old-name "already ran" check is applied in all three places: the head's checks, the history query (aliased earlierRuns) and the dispatch record. A head recorded under the old name is re-recorded under the new one.
    • Author exclusion uses numeric databaseId. It is typed with Number.isSafeInteger, so a string id is rejected, and an author with no id is skipped. The login-rename, null-author and string-id cases are tested.
    • LIST_QUERY selects databaseId on both Bot and User.
  • Manifests. No plugin.json or marketplace.json is touched, so no manifest <field> lines apply. The harness-ops change is one default list entry in morning-brief.sh, with bump: patch. The pr-pipeline fragment is bump: minor. Both levels fit.
  • Closing issues. The PR says Refs, not Closes, so the context file has no closing-issue sections. feat(pr-pipeline): restore Refine as its own stage that tidies every PR before review #9306 and pr-unblock: derive fix-ci-subscription's start and the restart cap from trusted values #8890 stay open by design, and the live apply-docs-patch canary is explicitly not yet run. No embedded instructions to the reviewer were found in the PR body.
  • Paved paths and path-scoped rules. I did not resolve the standards index or the .claude/rules/ files, so I make no claim about them.

Observation, not a finding

For one deploy window, a pr-unblock fix-docs #N run that is still in flight and has posted no check yet is not seen by the new in-flight match, which looks only at pr-refine titles. That PR could get a second fix-docs run under pr-refine. It is bounded to runs in flight at deploy and limited by limits.fix-docs-per-run, so I did not count it as a finding.

Hold: cost

.github/workflows/pr-refine.yml is a new workflow that starts model runs: the fix-docs and fix-docs-subscription jobs. fix-docs-dispatch.mjs and pr-unblock-dispatch.yml drop the person-only filter, so fix-docs now runs on lanes App PRs and on other bots' PRs as well. The only exclusions are Dependabot, the release App and the standards-sync App. The PR cites owner decisions Q38, Q42 and D8 for this spend, and melodic-software/azure-iac#80 as deployed. I can't verify either claim from here, so a person should confirm the spend before merge.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

claude-security-review reviewed f0f60705b12f5fe68965d09b5d325ad51434d8a7: 0 finding(s), 0 as inline comments.

Security review: PR #9332 (head f0f6070)

I found no security issues in this change. I read the full diff (all 23 files) and the PR context. critical-findings: 0

No plugin manifest changed (plugin.json and marketplace.json are untouched), so there are no manifest <field> lines. Nothing under .github/standards/trusted-actors/**, .github/actions/check-trusted-trigger/**, CODEOWNERS or .gitmodules changed, so no hold applies.

What I checked:

  • fix-docs-handoff and fix-docs-subscription (.github/workflows/pr-refine.yml):
    • The fallback leg no longer starts on fix-docs run-job outputs, which head code could forge.
    • It starts only on fix-docs-handoff outputs. That job has actions: read only, no checkout and no secrets, and it reads the Actions jobs API.
    • It matches whole job names (fix-docs / report, fix-docs / broker-check) with grep -qxF. A forged step name inside the head-code run job can't match, because only the base-code report and broker jobs are matched.
    • A failed read leaves both outputs empty and starts no fallback. The fix-ci fallback behaves differently here, since it runs on a failed read.
    • This is a hardening change.
  • apply-docs-patch:
    • It takes patch-state, patch-sha256, patch-attempt and reply-attempt only from the answering leg's scan-* outputs. Those come from the scan job, not the head-code run job.
    • The head comes from fix-docs-head, a base-code job.
    • It runs only on a written patch. apply-patch still applies the default row's protected-path and 300-path refusals.
    • Tests pin all of this.
  • Dispatch gate (pr-run-activity-read.yml and pr-run-activity-write.yml):
    • dispatched-workflow is now pr-refine.yml only when inputs.lane == 'pr-refine', and pr-unblock.yml otherwise.
    • Callers pass lane as a literal, so a dispatch can't select a different workflow than the lane's own.
    • The pr-unblock lane still admits only pr-unblock.yml.
  • Author filter (fix-docs-dispatch.mjs):
    • The person-only filter is replaced by an exclusion on numeric databaseId. A renamed login can't bypass it.
    • An author with no id, or with a non-integer id, is skipped (fails closed).
    • The change widens the set of PRs that get a model run to include lane-App and other bot PRs. The PR records this as an owner decision (Q38/Q42).
    • The PRs are still same-repository only, which needs write access.
    • The model job holds no write token, and its patch goes through apply-patch's protected-path refusals.
    • I did not independently confirm that the default patch-rules row covers .github/workflows/**, because that file is not in this diff. That is not a finding.
  • Check-name migration: the "already ran" test accepts both pr-refine / fix-docs and pr-unblock / fix-docs. This only suppresses re-dispatch and does not widen any grant.
  • Permissions: pr-refine.yml sets permissions: {} at the top. Each job grants only what the equivalent pr-unblock jobs had. run-name and concurrency use inputs.* only as strings, never in a shell. fix-docs-head validates pr-number before use.
  • Collateral files: lane-funding.json, docs, the morning-brief.sh list entry and the tests carry no security surface.

The instruction-exception register was not consulted. No standing instruction (CLAUDE.md, a rules file, a skill or agent body, or a hook allowlist) is deleted or narrowed in the diff. The removed author filter is script logic, and I judged it above.

@melodic-automation-lanes melodic-automation-lanes Bot added the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 23, 2026
@melodic-automation-lanes

Copy link
Copy Markdown
Contributor

The pr-unblock lane stopped on this pull request and needs a person: the merge stages a change under .github/workflows/, which the lane does not push.

  • Reason: workflow-staged
  • Resume key: ad8fac8099134c3a9fa672c8f516b14385933887 (the head the lane stopped on)

To resume, deal with the reason above, remove the needs-human label (and do-not-merge, if set), then dispatch the pr-unblock workflow with pr-number=9332 and activity=resolve-conflicts:

gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=9332 -f activity=resolve-conflicts

kyle-sexton and others added 2 commits October 23, 2026 02:18
# Conflicts:
#	.github/actions/resolve-config/callers.test.mjs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton

Copy link
Copy Markdown
Member Author

Reply to the claude-review summary on ad8fac80:

# Finding Classification Evidence Fix
1 docs/conventions/loop-lane/posting-lanes.md still names fix-docs and fix-docs-subscription as pr-unblock jobs and has no pr-refine row VALID (fix now) The read-runner row named those jobs under pr-unblock with run group pr-unblock-<activity>-<pr>. pr-refine.yml sets group: pr-refine-${{ inputs.activity }}-${{ inputs.pr-number }}, and its jobs queue on pr-pipeline-write-<pr> 808cdc5 adds two pr-refine rows (the write activity and the dispatching workflow). It names pr-refine as a caller of both runners and says pr-unblock-dispatch also starts pr-refine runs

The security review reported no findings, and Codex's code review reported none either.

The head moved since the review. Besides the fix, f9c82ae1 merges origin/main (#9307 among others). It resolves one conflict in callers.test.mjs: the read-runner caller list keeps main's two review-dependency callers and keeps fix-docs under pr-refine.yml. On the merged tree, the node suites under .github pass 3115 with 0 failures.

Comment thread .github/scripts/pr-merge/refire-refused.mjs Outdated
… refire

A pr-refine run is a workflow_dispatch on main, so an empty commit on the
PR cannot make it pass; it only restarts CI and the review lanes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton kyle-sexton added needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. and removed needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. labels Oct 23, 2026
kyle-sexton and others added 3 commits October 23, 2026 03:34
fix-docs-subscription started on fix-docs's lane-class and
credential-present outputs, which its head-code run job can forge. A
fix-docs-handoff job now runs fix-ci-handoff's jobs-API read, and the
fallback starts only when the broker allowed fix-docs and its report
posted no check.

Refs: #8890

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton

Copy link
Copy Markdown
Member Author

Reply to the claude-review summary on 1f96c116:

# Finding Classification Evidence Fix
1 Closes #9306 would close the issue with the canary criterion unmet VALID (fix now) #9306's last box is "One live apply-docs-patch commit lands on a canary PR". A pr-refine run is a workflow_dispatch on main's tip, and pr-refine.yml is not on main until this merges, so the canary can only run after merge The PR body now opens with Refs: #9306 and no longer closes it. The Verification section says to close #9306 with the canary commit's SHA once it lands. No code change

kyle-sexton and others added 2 commits October 23, 2026 04:32
# Conflicts:
#	.github/actions/resolve-config/callers.test.mjs
#	docs/conventions/pr-pipeline/pr-run-activity.md
fix-docs-subscription no longer reads a fix-docs output anywhere, so the
run-job-output test drops its pending entry and lists the leg under
pr-refine.yml.

Refs: #8890

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

claude-review holds this pull request for a person (cost) at a6e77b3c687e48e0d251b98683c9cae0db21c17c.

.github/workflows/pr-refine.yml is a new workflow that starts model runs (fix-docs and fix-docs-subscription, each at timeout-minutes: 60). .github/scripts/pr-unblock/fix-docs-dispatch.mjs also widens who gets those runs: it drops the person-only filter, so fix-docs now runs on every ready Markdown PR, lane-App PRs included. Only Dependabot, the release App and the standards-sync App are excluded. The PR body says the owner accepted this spend (Q38, Q42, D8, at the existing cap of 2 per push), but I could not verify that from here. A person should confirm the spend before merge.

claude-lane: claude-review

apply-docs-patch took patch-state, patch-sha256, patch-attempt,
reply-attempt and the head from fix-docs run-job outputs, which head code
can forge. It now takes one leg and that leg's scan-* outputs, and the
head from fix-docs-head, as fix-ci's apply-patch does.

Refs: #8890

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

claude-review holds this pull request for a person (cost) at f0f60705b12f5fe68965d09b5d325ad51434d8a7.

New workflow .github/workflows/pr-refine.yml adds model-run jobs (fix-docs, fix-docs-subscription), and .github/scripts/pr-unblock/fix-docs-dispatch.mjs removes the person-only author filter, so fix-docs now also runs on lanes App PRs and other bot PRs, excluding only Dependabot, the release App and the standards-sync App. This widens model spend. The PR cites owner decisions Q38, Q42 and D8 and says azure-iac#80 is deployed; a person should confirm both before merge.

claude-lane: claude-review

kyle-sexton added a commit that referenced this pull request Oct 23, 2026
A hold comment can be edited or deleted by anyone with write access, so a
trust-gate hold edited to read `criteria=cost` would have been cleared. The
step now reads every hold from the `claude-lane-hold` notice each lane's
status job records, keeps the label when the lanes App commented or a
comment was deleted in the window, and needs `checks: read`. It also
accepts a decision on an issue the body names on a `Refs:` line, which is
how #9332 cites #9306.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton kyle-sexton removed the needs-human Human-in-the-loop required; autonomous sessions must not resolve items carrying this. label Oct 23, 2026
@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 23, 2026
Merged via the queue into main with commit 352dcfb Oct 23, 2026
61 of 63 checks passed
@kyle-sexton
kyle-sexton deleted the feat/pr-refine-restore branch October 23, 2026 14:59
kyle-sexton added a commit that referenced this pull request Oct 23, 2026
…s-on-run-claude-cli

Compose callers.test.mjs: keep main's move of fix-docs to pr-refine.yml
(#9332), with fix-docs in SCAN_ONLY and HANDOFF, and this branch's
resolve-conflicts read-runner, scan-only and handoff entries. Compose
pr-pipeline/README.md: main's takeover-set HIGH floor (#9413) with this
branch's merge-base path and blob caps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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