chore: sync workflow templates - #5216
Conversation
Automated sync from stranske/Workflows Template hash: 1e3e9c91aafb Changes synced from sync-manifest.yml
|
Health 45 Agents Guard stopped this pull request. What we found
Next steps
Files seen in this run
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f692280c1
ℹ️ 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".
| expected-labels: >- | ||
| agent:auto,agent:codex,agent:claude,agent:copilot, | ||
| agents:auto-pilot,agents:keepalive | ||
| expected-actions: opened,synchronize,labeled |
There was a problem hiding this comment.
Include all guard-triggered actions in eligibility gate
Expand the expected-actions list here to cover every action this workflow subscribes to (reopened, ready_for_review, and unlabeled), otherwise those events are always denied by agent-event-eligibility and the guard steps never run. That means the safety/status logic is skipped on legitimate guard triggers (for example, reopening a PR or removing an agent label), which can leave protected-file enforcement and guard status stale.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR syncs the repository’s GitHub Actions “consumer” workflows and supporting scripts/docs from stranske/Workflows (SHA 88aaa4f4dfde4c4c0f3c68fcc286709cea127eca), consolidating several deprecated agent workflows into the newer “event hub” + “gate followups” architecture while adding new gating primitives (event eligibility + state fingerprinting) to reduce unnecessary runs.
Changes:
- Replaces legacy standalone agent workflows (PR meta, keepalive loop, autofix loop, verify-to-issue) with consolidated workflows:
agents-80-pr-event-hub.ymlandagents-81-gate-followups.yml. - Introduces state fingerprint gating (
scripts/state_fingerprint.py) and applies it to agent workflows to skip unchanged-state re-runs. - Adds reusable composite actions/config for path classification and event eligibility; updates docs to reference the new consolidated workflows.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| WORKFLOW_USER_GUIDE.md | Updates troubleshooting references to consolidated workflows. |
| scripts/sync_test_dependencies.py | Updates stdlib allowlist used for dependency syncing. |
| scripts/state_fingerprint.py | Adds a new helper for workflow “unchanged-state” fingerprint gating. |
| docs/LABELS.md | Updates workflow references for label-driven automation. |
| docs/CODEX_TOKEN_REFRESH.md | Updates token refresh verification command to new workflow entrypoint. |
| .github/workflows/autofix.yml | Adds event eligibility gating and safer job outputs for skipped context. |
| .github/workflows/agents-verify-to-issue.yml | Removes deprecated legacy verify-to-issue workflow. |
| .github/workflows/agents-verify-to-issue-v2.yml | Removes deprecated v2 verify-to-issue workflow (replaced by event hub). |
| .github/workflows/agents-verifier.yml | Adds state fingerprint gate to avoid redundant verifier runs. |
| .github/workflows/agents-pr-meta.yml | Removes deprecated PR meta workflow (replaced by event hub). |
| .github/workflows/agents-keepalive-loop.yml | Removes deprecated keepalive loop workflow (replaced by gate followups). |
| .github/workflows/agents-keepalive-loop-reporter.yml | Adds state fingerprint gating to reduce duplicate reporter posts. |
| .github/workflows/agents-guard.yml | Adds event eligibility gating to avoid unnecessary guard runs. |
| .github/workflows/agents-bot-comment-handler.yml | Removes deprecated bot-comment handler workflow (replaced by event hub). |
| .github/workflows/agents-autofix-loop.yml | Removes deprecated autofix loop workflow (replaced by gate followups). |
| .github/workflows/agents-autofix-dispatcher.yml | Converts dispatcher into an acknowledgement/bridge for consolidated handling. |
| .github/workflows/agents-auto-pilot.yml | Routes orchestration dispatches to consolidated workflows and adds eligibility gating. |
| .github/workflows/agents-auto-label.yml | Adjusts issue triggers and adds eligibility gating to avoid bot-trigger loops. |
| .github/workflows/agents-81-gate-followups.yml | Consolidates keepalive/autofix followups and adds state fingerprint gating. |
| .github/workflows/agents-80-pr-event-hub.yml | Adds event eligibility gating and conditions for consolidated handlers. |
| .github/scripts/keepalive_orchestrator_gate_runner.js | Enhances keepalive gate runner (label self-heal, draft disposition handling, gate run inspection). |
| .github/scripts/gate_summary.py | Adds python_required support for path-classifier-driven gating. |
| .github/scripts/detect-changes.js | Hardens changed-file classification input handling and makes rate-limit wrapper best-effort. |
| .github/path-classification.yml | Adds repo path classification configuration for workflow gating. |
| .github/actions/path-classifier/action.yml | Adds composite action for classifying changed paths into categories. |
| .github/actions/path-classifier/classify.js | Implements path classification logic for the composite action. |
| .github/actions/agent-event-eligibility/action.yml | Adds composite action for early event eligibility gating. |
| .github/actions/agent-event-eligibility/eligibility.js | Implements custom predicate evaluation + allow/deny gates for events/actors/labels. |
Comments suppressed due to low confidence (1)
.github/workflows/agents-81-gate-followups.yml:970
attemptCountis now derived from prior runs ofagents-81-gate-followups.yml. Since this workflow also handles non-autofix followups (and can run multiple times per head SHA for reasons unrelated to an autofix attempt), this risks inflating the autofix attempt counter and hitting the max-attempts gate prematurely. Consider tracking autofix attempts separately (e.g., via a dedicated marker/comment/variable) or filtering runs to only those that actually executed the autofix path.
const workflowFile = 'agents-81-gate-followups.yml';
// Reduce attempts for auto-escalated PRs (they weren't agent-initiated)
const isEscalated = labels.includes('autofix:escalated');
const maxAttempts = isEscalated
? Math.min(2, Number(outputs.max_attempts))
: Number(outputs.max_attempts);
const previousRuns = await paginateWithRetry(github.rest.actions.listWorkflowRuns, {
owner,
repo,
workflow_id: workflowFile,
head_sha: run.head_sha,
per_page: 100,
status: 'completed',
| try { | ||
| github = await ensureRateLimitWrapped({ github: rawGithub, core, env: process.env }); | ||
| } catch (error) { | ||
| core?.warning?.(`Failed to enable rate-limit wrapper for detect-changes: ${error.message}`); |
| required: false | ||
| default: '' | ||
| custom-predicate: | ||
| description: JMESPath-style predicate evaluated against the event payload. Must be truthy when supplied. |
|
|
||
| if not payload: | ||
| return None | ||
| return json.loads(payload) |
| return args.func(args) | ||
| except RuntimeError as exc: | ||
| print(str(exc), file=sys.stderr) | ||
| return 1 |
| def compute_fingerprint(workflow_name: str, inputs: dict[str, Any]) -> str: | ||
| payload = { | ||
| "workflow": workflow_name, | ||
| "inputs": inputs, | ||
| "version": MARKER_VERSION, | ||
| } | ||
| canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True) | ||
| return hashlib.sha256(canonical.encode("utf-8")).hexdigest() | ||
|
|
||
|
|
||
| def compare_fingerprint( | ||
| workflow_name: str, | ||
| current_inputs: dict[str, Any], | ||
| storage: FingerprintStorage, | ||
| ) -> Decision: | ||
| current_hash = compute_fingerprint(workflow_name, current_inputs) | ||
| prior_hash = storage.read_fingerprint(workflow_name) | ||
|
|
||
| if prior_hash is None: | ||
| return Decision(True, "no-prior-fingerprint", current_hash, None) | ||
| if prior_hash == current_hash: | ||
| return Decision(False, "fingerprint-match", current_hash, prior_hash) | ||
| return Decision(True, "fingerprint-changed", current_hash, prior_hash) | ||
|
|
||
|
|
||
| def store_fingerprint( | ||
| workflow_name: str, | ||
| fingerprint_hash: str, | ||
| storage: FingerprintStorage, | ||
| ) -> None: | ||
| storage.write_fingerprint(workflow_name, fingerprint_hash) | ||
|
|
||
|
|
||
| def _utc_now() -> str: | ||
| return dt.datetime.now(dt.UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z") | ||
|
|
||
|
|
||
| def _marker_re(workflow_name: str) -> re.Pattern[str]: | ||
| escaped = re.escape(workflow_name) | ||
| return re.compile( | ||
| rf"<!--\s*{MARKER_PREFIX}:{escaped}:{MARKER_VERSION}\s+(\{{.*?\}})\s*-->", | ||
| re.DOTALL, | ||
| ) | ||
|
|
||
|
|
||
| def _build_marker(workflow_name: str, fingerprint_hash: str) -> str: | ||
| payload = {"hash": fingerprint_hash, "ts": _utc_now()} | ||
| return ( | ||
| f"<!-- {MARKER_PREFIX}:{workflow_name}:{MARKER_VERSION} " | ||
| f"{json.dumps(payload, sort_keys=True, separators=(',', ':'))} -->" | ||
| ) | ||
|
|
||
|
|
||
| def _extract_hash(value: str | None, workflow_name: str) -> str | None: | ||
| if not value: | ||
| return None | ||
|
|
||
| candidates: list[str] = [] | ||
| marker_match = _marker_re(workflow_name).search(value) | ||
| if marker_match: | ||
| candidates.append(marker_match.group(1)) | ||
| stripped = value.strip() | ||
| if stripped.startswith("{"): | ||
| candidates.append(stripped) | ||
|
|
||
| for candidate in candidates: | ||
| try: | ||
| payload = json.loads(candidate) | ||
| except json.JSONDecodeError: | ||
| continue | ||
| fingerprint_hash = payload.get("hash") if isinstance(payload, dict) else None | ||
| if isinstance(fingerprint_hash, str) and re.fullmatch(r"[0-9a-f]{64}", fingerprint_hash): | ||
| return fingerprint_hash | ||
| return None |
| diff_surface = [ | ||
| { | ||
| "filename": item.get("filename", ""), | ||
| "status": item.get("status", ""), | ||
| "sha": item.get("sha", ""), | ||
| "additions": item.get("additions", 0), | ||
| "deletions": item.get("deletions", 0), | ||
| "changes": item.get("changes", 0), | ||
| } | ||
| for item in files | ||
| ] |
|
Closing as stale because a newer sync workflow templates PR exists for this repository in the current cleanup campaign. |
Sync Summary
Files Updated
Files Skipped
Review Checklist
Source: stranske/Workflows
Source SHA:
88aaa4f4dfde4c4c0f3c68fcc286709cea127ecaTemplate hash:
1e3e9c91aafbSync branch:
sync/workflows-1e3e9c91aafbConsumer repo:
stranske/Trend_Model_ProjectManifest:
.github/sync-manifest.yml