Add executable release/* promotion path + CI-tip gate (#4000 follow-up)#4122
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe PR extends the release gate in ChangesCI-branch-aware release gate
Release-phase-aware address-review skill
Sequence DiagramssequenceDiagram
participant ReleaseTask as :release task
participant Validate as validate_main_ci_status!
participant Fetch as fetch_main_ci_checks
participant GitHub as GitHub API
ReleaseTask->>Validate: ci_branch: "release/X.Y.Z" or "main"
Validate->>Fetch: ci_branch: "release/X.Y.Z"
Fetch->>GitHub: git fetch origin/release/X.Y.Z
Fetch->>GitHub: check-runs for resolved SHA
GitHub-->>Fetch: check run results
Fetch-->>Validate: checks
Validate->>GitHub: branch-protection required checks for ci_branch (URI-encoded)
GitHub-->>Validate: required check names
Validate-->>ReleaseTask: pass or abort with origin/release/X.Y.Z in message
flowchart LR
A["address-review Step 2.5:<br/>gh pr view --json baseRefName"] --> B{base branch?}
B -->|"release/*"| C["RELEASE_PHASE = rc/final"]
B -->|"main / other"| D["RELEASE_PHASE = beta"]
C --> E["Suppress autonomous<br/>optional-nit rule"]
D --> F["Allow autonomous<br/>optional-nit handling"]
E --> G["Action f:<br/>skip autonomous OPTIONAL step"]
F --> H["Action f:<br/>run autonomous OPTIONAL step"]
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.
Reviewed by Cursor Bugbot for commit 19c7059. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 19c7059c27
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb (1)
2328-2332: ⚡ Quick winAssert the encoded branch segment in this endpoint stub.
This should expect
release%2F17.0.0rather than rawrelease/17.0.0, so the spec protects the intended API contract.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb` around lines 2328 - 2332, The test stub for the Open3.capture2e method is using a raw branch name "release/17.0.0" in the endpoint URL, but it should use the URL-encoded version "release%2F17.0.0" to match the actual API contract. Update the endpoint path in the .with() call of the Open3.capture2e stub to replace "repos/shakacode/react_on_rails/branches/release/17.0.0/protection/required_status_checks" with "repos/shakacode/react_on_rails/branches/release%2F17.0.0/protection/required_status_checks" so the test properly asserts the expected encoded branch segment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.agents/skills/address-review/SKILL.md:
- Around line 96-114: The BASE_REF assignment with `|| echo ""` masks command
failures, causing any gh pr view error to silently default to
RELEASE_PHASE="beta" even on release/* branches. This creates a "fail closed"
problem where transient auth/network issues incorrectly enable autonomous
optional-nit handling on stabilizing branches. Separate the gh pr view command
execution to explicitly detect when it fails, then implement the conditional
logic described in the requirements: only default to beta if the base is
confirmed to be main; otherwise, report that the phase could not be resolved and
ask the user to confirm the phase before proceeding with nit-autonomy rules.
In @.agents/workflows/address-review.md:
- Around line 91-98: The BASE_REF assignment using gh pr view with a fallback to
empty string silently masks lookup failures, causing the case statement to
default to RELEASE_PHASE=beta even when the PR targets a release/* branch. This
bypasses the stricter rc/final requirements. Replace the || echo "" fallback
with explicit error handling that exits or fails when the gh pr view command
fails or returns empty, ensuring the workflow does not silently downgrade
release/* PRs to the more permissive beta phase.
In `@rakelib/release.rake`:
- Around line 848-851: The `ci_branch` parameter in the
`required_check_names_for_main` method is being directly interpolated into the
GitHub API path without URL encoding, causing branch names containing forward
slashes (like `release/17.0.0`) to break the endpoint path. Use `CGI.escape()`
to properly encode the `ci_branch` before it is interpolated into the `api_path`
string to ensure special characters are handled correctly by the GitHub REST
API.
---
Nitpick comments:
In `@react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb`:
- Around line 2328-2332: The test stub for the Open3.capture2e method is using a
raw branch name "release/17.0.0" in the endpoint URL, but it should use the
URL-encoded version "release%2F17.0.0" to match the actual API contract. Update
the endpoint path in the .with() call of the Open3.capture2e stub to replace
"repos/shakacode/react_on_rails/branches/release/17.0.0/protection/required_status_checks"
with
"repos/shakacode/react_on_rails/branches/release%2F17.0.0/protection/required_status_checks"
so the test properly asserts the expected encoded branch segment.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: eb83fadf-3eff-4a33-a08e-1d1340b09401
📒 Files selected for processing (6)
.agents/skills/address-review/SKILL.md.agents/workflows/address-review.mdAGENTS.mdinternal/contributor-info/release-train-runbook.mdrakelib/release.rakereact_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb
Greptile SummaryThis PR closes two release-critical gaps in the release-train workflow: it allows
Confidence Score: 4/5Safe to merge; the core release-path logic is correct and well-tested. The one finding is a fallback that is actually more conservative than intended, not less. The branch-allowed guard and CI branch selection work correctly end-to-end. The only issue is in rakelib/release.rake — specifically the Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["rake release[X.Y.Z]"] --> B{is_prerelease?}
B -- "yes (rc, beta, etc.)" --> C[any branch allowed]
B -- "no (stable)" --> D{stable_release_branch_allowed?}
D -- "main OR release/X.Y.Z" --> E[allowed]
D -- "other branch" --> F[abort ❌]
E --> G["release_ci_branch(current_branch)"]
C --> G
G -- "starts with release/" --> H["ci_branch = release/X.Y.Z"]
G -- "other" --> I["ci_branch = main"]
H --> J["validate_main_ci_status!(ci_branch: release/X.Y.Z)"]
I --> K["validate_main_ci_status!(ci_branch: main)"]
J --> L["fetch origin/release/X.Y.Z tip SHA"]
K --> M["fetch origin/main tip SHA"]
L --> N["required_check_names_for_main(ci_branch: release/X.Y.Z)"]
M --> O["required_check_names_for_main(ci_branch: main)"]
N --> P{API path URL-encoded?}
P -- "No (current)" --> Q["gh api returns 404 → nil fallback\n(evaluate ALL checks)"]
P -- "Yes (fix)" --> R["correct required-checks filter"]
Q --> S[CI gate passes / aborts]
R --> S
O --> S
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["rake release[X.Y.Z]"] --> B{is_prerelease?}
B -- "yes (rc, beta, etc.)" --> C[any branch allowed]
B -- "no (stable)" --> D{stable_release_branch_allowed?}
D -- "main OR release/X.Y.Z" --> E[allowed]
D -- "other branch" --> F[abort ❌]
E --> G["release_ci_branch(current_branch)"]
C --> G
G -- "starts with release/" --> H["ci_branch = release/X.Y.Z"]
G -- "other" --> I["ci_branch = main"]
H --> J["validate_main_ci_status!(ci_branch: release/X.Y.Z)"]
I --> K["validate_main_ci_status!(ci_branch: main)"]
J --> L["fetch origin/release/X.Y.Z tip SHA"]
K --> M["fetch origin/main tip SHA"]
L --> N["required_check_names_for_main(ci_branch: release/X.Y.Z)"]
M --> O["required_check_names_for_main(ci_branch: main)"]
N --> P{API path URL-encoded?}
P -- "No (current)" --> Q["gh api returns 404 → nil fallback\n(evaluate ALL checks)"]
P -- "Yes (fix)" --> R["correct required-checks filter"]
Q --> S[CI gate passes / aborts]
R --> S
O --> S
Reviews (1): Last reviewed commit: "Add executable release/* promotion path ..." | Re-trigger Greptile |
|
Code Review Overview This PR closes two documented release-train gaps: (1) allowing rake release[X.Y.Z] to run from a matching release/X.Y.Z branch (stable_release_branch_allowed?), and (2) making the CI gate check the tip of whichever branch is actually being released (release_ci_branch + ci_branch threading). The address-review skill gains Step 2.5 to derive the release phase from the PR base branch. The implementation is focused and the 12 new RSpec examples cover the new helpers well. Correctness
Issues / Suggestions
branch_kwargs = ci_branch == 'main' ? {} : { ci_branch: } This avoids touching ~130 existing test stubs (which match fetch_main_ci_checks without ci_branch:) by not splatting the kwarg when it equals the default. It works today, but if the default of ci_branch: is ever changed, the main path will silently pass the old value rather than the new default. The comment in the code explains the reasoning, but this is worth a follow-up to either update the stubs or add a TODO noting the fragility. Inline comment added below.
The value set is 'rc' but the comment says 'treat as final'. The surrounding prose explains the floor/ceiling distinction (rc is the floor; the tracker controls whether it is actually final), but the inline comment alone reads as a bug. A clearer phrasing would be: '# floor=rc; treat as final when tracker is in final-release mode'. Inline comment added below.
SKILL.md uses three case arms (release/*, main|'', ); the workflow copy uses two (release/, *). They are semantically identical today, but they will drift with future edits to one file. Since the PR description says these are mirrored, they should be byte-for-byte identical in this snippet.
The error message was changed from 'Release must be run from the main branch!' to a multi-line message that now includes Target version: and the release-branch option. No new or updated spec examples assert on this string. Not a blocker (tests are green), but a regression guard would be useful. Security No concerns. ci_branch is always either 'main' or a branch name that passed the stable_release_branch_allowed? whitelist before release_ci_branch is invoked. Pre-release paths always return 'main'. All git/gh calls pass ci_branch as a separate array element (no shell interpolation). The GitHub API path interpolation goes through gh api and the branch name is whitelist-constrained. Test Coverage Helper-level coverage is solid: stable_release_branch_allowed? (4 cases), release_ci_branch (3 cases), fetch_main_ci_checks with override (2 cases), validate_main_ci_status! with override (2 cases), required_check_names_for_main with override (1 case). The one gap is the task-level integration (the wiring of release_ci_branch(current_branch) into validate_main_ci_status!) which is verified by reading the code but not by a spec. Overall: the change is sound and fills the documented gaps correctly. The items above are all minor. |
Code Review — PR #4122Overall: ✅ Solid implementation, minor nits below. What this PR doesCloses two release-critical gaps in the release-train workflow:
Strengths
Minor issues
Functionality looks correct. The release-promotion invariant (promote the last good RC in-place, no re-cut from |
The release-promotion review fix left the `f` and `f+i` lines at 5 leading spaces while `f+o`…`m` use 6, misaligning the quick-action table. Bump both to 6 spaces (claude-review on PR #4122). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4941b0bb54
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d66fe1fe-77d1-47f8-a651-f1f860a12e43) |
|
test |
|
+ci-force-full |
Force-Full Hosted CI RequestedTriggered 9 workflow(s) for View progress in the Actions tab. |
Code Review — PR #4122: Executable release/* promotion path + CI-tip gateOverall: well-scoped, backward-compatible, and well-tested. The two-helper approach ( 1.
|
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_30ecfe0f-f8da-420f-9ca6-68190c09a0c7) |
|
PR Review: Release Branch Promotion Path\n\nOverall this is a well-structured PR. The implementation is thorough: multiple layers of defense (identity check, RC tag ancestry walk, metadata-only commit classifier, version policy scoping), 197 tests, and careful error messages that tell the releaser exactly what to do next. The CI selector changes and deleted-push guards are handled correctly across all workflows. A few findings below.\n\nMedium: Net::HTTP.get_response has no timeout\n\nfetch_rubygems_versions (new) uses Net::HTTP.get_response without an open_timeout or read_timeout. If rubygems.org is slow or hangs, the release task will block indefinitely at the idempotency pre-check with no retry and no warning. The existing publish-verify path uses npm view (a CLI with system timeout defaults), so this is a new failure mode. Fix: replace get_response with Net::HTTP.start(..., open_timeout: 10, read_timeout: 15) { |http| http.get(uri.request_uri) }.\n\nLow: --publish-branch is a no-op alongside --no-git-checks\n\nIn npm_publish_base_args, a stable release-branch publish gets both --no-git-checks (skips all git validations) and --publish-branch branch (configures which branch pnpm would check -- already disabled). Harmless but dead code; a future reader may assume it provides an active guard.\n\nAdvisory: RELEASE_FINALIZATION_METADATA_PATHS omits CHANGELOG.md without explanation\n\nThe comment says Keep in sync with every file the release task rewrites but CHANGELOG.md is intentionally excluded (changelog-only commits pass through commit_non_runtime_only? instead). A one-line note would prevent a maintainer from adding it here and breaking the classifier -- release_finalization_metadata_content_only? has no .md handler and would raise UnhandledReleaseFinalizationMetadataPathError.\n\nAdvisory: RC cut on an old release line is silently blocked when a newer stable exists\n\nWith release_branch_tag_scope: true, tagged_version_order_candidates excludes other-line prereleases but keeps all stable versions. If 17.1.0 is already tagged and someone runs rake release[17.0.0.rc.4] from release/17.0.0 (security backport), the version policy fires with must be greater than latest tagged version 17.1.0. The existing test only covers a newer prerelease (17.1.0.beta.1), not a newer stable. Likely intentional given PR scope, but undocumented.\n\nNit: release_branch_tag_scope defaults to the parameter release_branch_final_promotion\n\nThe default release_branch_tag_scope: release_branch_final_promotion is valid Ruby but uncommon enough that a reader might think it is a mistake. A brief comment on the method would clarify intent.\n\n---\n\nTest suite quality is high, especially the release_finalization_metadata_commit? tests (runtime paths, non-modification entries, dependency-only lock changes, unhandled-path raise, and package-path sync guard). The identity check (ensure_release_branch_head_matches_remote!) has no direct unit tests but is exercised end-to-end through validate_main_ci_status!; acceptable for release tooling. |
Adversarial PR Review — #4122PR: Add executable release/* promotion path + CI-tip gate SummaryThis PR makes the release-train RC-to-stable promotion path runnable from a BLOCKINGNone. DISCUSS1.
The regex only replaces In practice this is conservative: CI is evaluated on the finalization commit (the one that will be tagged), not a stale ancestor. The risk is that if no CI run has completed on the finalization SHA specifically, the gate returns Recommendation: Not a blocker. The existing runbook hint ("wait for at least one CI run to complete before retrying") covers this. Consider adding a note that broader Gemfile.lock changes also stop the walkback on the finalization commit. FOLLOWUP2.
3. No unit test for The mismatch path (local != remote SHA) is well-tested at spec lines 2360-2397. The HEAD-unresolvable failure path is untested — it delegates to NON_BLOCKING_DECISION4.
The function deliberately does not accept 5.
Both flags are emitted for stable publishes from Review-Thread Disposition
Validation Evidence
Merge authority: not delegated — requires maintainer decision. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4f0b3921ab
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Code Review — Release promotion path + CI-tip gateOverall: This is a well-engineered, well-tested PR. The release-branch promotion path is guarded at multiple layers (branch name check → cross-line guard → RC tag ancestry + metadata-only walk → local/remote HEAD identity → CI gate on the correct ref), and the test suite covers the new branches thoroughly. The design choices are sound. A few advisory notes below. Correctness
Workflow / CIThe The selector action change narrows the Inline commentsThree advisory notes are left inline (all low-risk):
None of these are blocking. The release-critical logic is correct. |
Code Review {4122Overall AssessmentThis is a well-structured PR with solid test coverage (197 examples). The release-branch promotion path is implemented carefully, with layered safety checks (branch-name–version match, remote RC tag ancestry, metadata-only walk-back, local/remote HEAD alignment, idempotent registry skip). The logic in Summary of findings (see inline comments for detail)
The core promotion logic (CI gate, RC ancestry check, metadata-only walk, idempotent retry, version-policy scope) is sound and the test coverage for the new paths is thorough. |
|
State: ready-no-merge-authority. Merge authority was not delegated for this batch, so I am not merging. Confidence note:
|

Summary
Follow-up to #4000 / #4018 for the release-train branching model. This PR makes the documented in-place RC-to-stable promotion path executable: a stable release can be promoted from the matching
release/X.Y.Zbranch, and the release CI gate validates the branch being released instead of always checkingorigin/main.Why
Before this PR, the release-train runbook described a path that the release task could not safely execute. The task was still shaped around
main, which made final promotion from the last good RC either blocked or dependent on manual bypasses. This PR keeps the scope to that release-critical path rather than adding forward-port automation.Implementation
rake release[X.Y.Z]frommainor exactlyrelease/X.Y.Z, while rejecting cross-line release branches.origin/release/X.Y.Zwhen releasing from a release branch, including forced release-branch fetches and non-overridable local/remote HEAD identity checks outside dry-run mode.release/*remain usable.release/**push triggers, deleted-push guards, and selector full-matrix behavior for release branch CI.Review Closeout
cad5895c5.70403c774; remaining advisory nits were auto-deferred and resolved to avoid another nit-only review cycle.Validation
actionlinton all edited workflow files: passed.cd react_on_rails && bundle exec rspec spec/react_on_rails/release_rake_helpers_spec.rb: 197 examples, 0 failures.cd react_on_rails && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop --cache false ../rakelib/release.rake spec/react_on_rails/release_rake_helpers_spec.rb --format simple: 2 files inspected, no offenses.pnpm start format.listDifferent: passed.script/ci-changes-detector origin/main: selected full suite, no benchmarks..agents/skills/pr-batch/bin/pr-ci-readiness 4122 --repo shakacode/react_on_rails: READY.script/pr-merge-ledger 4122 --repo shakacode/react_on_rails --changelog-classification not_user_visible --finding-dispositions /tmp/pr-4122-finding-dispositions.json --strict --pretty:complete_allowed: true.AI review note: local Codex review found release-promotion issues earlier in the cleanup and those were fixed. Later
codex review --base origin/mainwas blocked by the Codex review usage limit, andclaude ultrareview origin/main --timeout 10fallback was blocked by repeated HTTP 502s. GitHub Claude review comments on the current head have been handled or auto-deferred with rationale.Changelog
not_user_visible- release tooling, CI routing, and contributor documentation only.Confidence Note
Refs #4000, #4018
Note
High Risk
Changes release promotion, CI gating, and npm publish behavior on release branches—release-critical paths with large
release.rakesurface area; mitigated by extensive specs but post-merge workflow exercise is still tracked separately.Overview
Makes the release-train in-place RC → stable path real: stable
rake release[X.Y.Z]can run frommainor the matchingrelease/X.Y.Zbranch, with guards so you cannot promote across release lines or without the accepted remote RC.rakelib/release.rakenow validates CI onorigin/release/X.Y.Z(not alwaysmain), enforces local/remote release-branch HEAD alignment (non-overridable outside dry-run), walks back metadata-only finalization commits for the gate, and tightens version policy for release-branch finals (e.g. newer prerelease tags on another line). NPM publish args support stable publishes fromrelease/*.GitHub Actions add
release/**push triggers on hosted workflows, skip deleted branch pushes, and extendhosted-ci-selectorsso release-branch pushes count as release targets for hosted CI and broad matrices.Contributor docs (
AGENTS.md, runbook, CI optimization) are updated to match. Spec coverage inrelease_rake_helpers_spec.rbis expanded heavily.Reviewed by Cursor Bugbot for commit 70403c7. Bugbot is set up for automated code reviews on this repo. Configure here.
Merge Authority Update - 2026-06-24
Maintainer merge authority was delegated in the current Codex session with the condition that merge reasons are documented in the PR description. Reasons for merge:
Mode: development; this PR targetsmain, so the beta/main gate applies.4b3714c0c1363bb40927c2eb593c66f01407bb01is non-draft, mergeable, andmergeStateStatusisCLEAN.pr-ci-readinessisREADY; fullgh pr checksshows current-head CI complete with no failures or pending checks.complete_allowed: truewith no violations or unknown fields (/tmp/pr-4122-merge-ledger-20260624.json).not_user_visiblebecause this is release tooling, CI routing, and contributor documentation.SECURITY_PREFLIGHT_BLOCKEDfinding was explicitly acknowledged/waived by the maintainer before this closeout resumed.Confidence note:
/tmp/pr-4122-merge-ledger-20260624.json; workflow follow-up Follow-up: Exercise GitHub Actions changes from PR #4122 #4172; existing validation and workflow audit links above.