Skip to content

feat: Add PR Dashboard#500

Open
aceppaluni wants to merge 4 commits into
hiero-ledger:mainfrom
aceppaluni:prui
Open

feat: Add PR Dashboard#500
aceppaluni wants to merge 4 commits into
hiero-ledger:mainfrom
aceppaluni:prui

Conversation

@aceppaluni

@aceppaluni aceppaluni commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds a GitHub-action to present contributors with a PR dashboard that allows the to see the status of their commits. This dashboard updates on commit pushes ensuring that each PR meets the proper requirements for allowing commit signing and merging.

Changes Made

  • Added...
  • A script folder containing the bot use scripts executed by the corresponding YML files.
  • Helper methods were added to assist bot logic scripts and consolidate infrastructure.
  • Modified...
  • Fixed...

Related Issues

Fixes: #499

Screenshots (if applicable)

Checklist

  • Tests added/updated
  • Documentation updated
  • Linting passes
  • Branch up-to-date with main

Deployment Notes

Links to testing:

Summary by CodeRabbit

  • New Features
    • Introduced automated PR validation system that checks commit signatures, merge conflicts, and issue linkage
    • Auto-assigns pull request authors and dynamically manages PR status labels based on validation results
    • Posts automated summary comments on PRs with comprehensive check results
    • Added configurable automation settings for teams, labels, and skill hierarchies to support contributor workflows

Signed-off-by: aceppaluni <aceppaluni@gmail.com>
@aceppaluni aceppaluni requested review from a team as code owners June 12, 2026 19:34
@aceppaluni aceppaluni requested a review from leninmehedy June 12, 2026 19:34
@netlify

netlify Bot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for hiero-open-source ready!

Name Link
🔨 Latest commit a616ca3
🔍 Latest deploy log https://app.netlify.com/projects/hiero-open-source/deploys/6a2c66055a3e58000802d4c2
😎 Deploy Preview https://deploy-preview-500--hiero-open-source.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This pull request implements a complete GitHub bot system that validates pull requests against automation rules, posts standardized dashboard comments with validation results, and manages PR status labels. It includes configuration-driven check definitions, validation logic for commits and issue linking, safe GitHub API wrappers, and two workflows triggered on PR open and update events.

Changes

GitHub PR Automation Bot

Layer / File(s) Summary
Automation configuration and validation
.github/hiero-automation.json, .github/scripts/helpers/config-loader.js
Configuration file defines teams, label mappings, hierarchies, skill prerequisites, and assignment limits. Loader reads, validates structure (required keys, membership, uniqueness), and derives frozen constants for bot use.
Helper module infrastructure
.github/scripts/helpers/validation.js, .github/scripts/helpers/logger.js, .github/scripts/helpers/constants.js, .github/scripts/helpers/index.js
Validation predicates and throwing validators for bot inputs; logger creation with prefixing; centralized constants derived from config (ISSUE_STATE, hierarchies, teams); helper module aggregation.
PR validation checks
.github/scripts/helpers/checks.js
DCO sign-off and GPG signature regex validators; merge-conflict polling loop; issue-number extraction from PR body and title; assignee verification for linked issues; aggregated structured results with failure reporting.
GitHub API integration and orchestration
.github/scripts/helpers/api.js
Bot context validation from workflow inputs; safe mutation wrappers (labels, assignees, comments); paginated PR/issue fetches; GraphQL queries for closing references and milestone lookup; label swapping conditional on check results; orchestrated check execution and comment posting/updating.
Dashboard comment generation
.github/scripts/helpers/comments.js
Check result display state mapping (pass/fail/error); reusable section renderer; specialized builders for DCO, GPG, merge conflicts, issue-link assignment; unified comment with conditional footer based on pass/fail status.
Bot entrypoints and workflows
.github/workflows/on-pr.yml, .github/scripts/bot-on-pr-open.js, .github/workflows/on-pr-update.yml, .github/scripts/bot-on-pr-update.js
PR-open workflow triggers on open/reopen/ready-for-review; PR-update workflow triggers on synchronize/edited; both gate on draft status and use per-PR concurrency; handlers build context, run checks, post/update dashboard comment, and swap status labels with error handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Suggested reviewers

  • leninmehedy
  • exploreriii
  • rbarker-dev

Poem

🐰 Hopping through workflows with checks held high,
A bot dances gently as PRs fly by,
DCO and GPG, no merge-time despair—
Dashboard comments blooming with careful care.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements core dashboard functionality (PR monitoring scripts and checks), but does not meet the advanced-issue prerequisites: it lacks the required web UI components, Next.js pages, React/TypeScript implementation, comprehensive testing, and design documentation referenced in issue #499. Implement the missing PR Dashboard UI components in src/components/, Next.js pages in src/app/, comprehensive test coverage, and provide architectural design documentation to fully address issue #499 requirements.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Add PR Dashboard' directly and accurately summarizes the main change—adding a GitHub Actions PR dashboard for contributors.
Description check ✅ Passed The PR description covers the purpose, lists key changes (scripts and helpers), includes the related issue (#499), and completes most checklist items, though tests and documentation remain unchecked.
Out of Scope Changes check ✅ Passed All changes are in-scope GitHub Actions scripts and infrastructure; no unrelated modifications to existing code or out-of-scope file changes are present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lfdt-bot

lfdt-bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Signed-off-by: aceppaluni <aceppaluni@gmail.com>
Comment thread package.json Outdated
Signed-off-by: aceppaluni <aceppaluni@gmail.com>
Signed-off-by: aceppaluni <aceppaluni@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 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 @.github/scripts/bot-on-pr-update.js:
- Line 35: The PR update handler calls swapStatusLabel(botContext, allPassed)
without forcing reconciliation, so PRs that lack both status labels won't get
fixed on synchronize/edited events; update the call in
.github/scripts/bot-on-pr-update.js to pass the force flag (e.g.,
swapStatusLabel(botContext, allPassed, true) or the equivalent third argument
expected by swapStatusLabel) so the function runs a forced reconciliation of
labels when handling update events.

In @.github/scripts/helpers/api.js:
- Around line 670-673: runAllChecksAndComment currently awaits
postOrUpdateComment but ignores its success/failure so dashboard upsert failures
are dropped; change runAllChecksAndComment to capture the result/error from
postOrUpdateComment (call to postOrUpdateComment(botContext, marker, body)),
verify its returned success flag or catch and rethrow/provide a failure
indicator, and propagate that failure in the function return (e.g., include an
upsertSuccess/upsertError field or throw) instead of unconditionally returning {
allPassed } so status-label transitions cannot proceed when the dashboard
comment update failed.
- Around line 8-31: The imports are using named imports but the target modules
export only default objects; update the import contracts to import the default
and then destructure the needed symbols (e.g., in .github/scripts/helpers/api.js
import the default from './logger.js', './validation.js', './constants.js', and
'./comments.js' and then destructure getLogger, isSafeSearchToken,
requireObject, LABELS, SKILL_HIERARCHY, ISSUE_STATE, buildBotComment, etc.), and
likewise change .github/scripts/helpers/constants.js to import the default from
'./config-loader.js' and destructure the required config values; alternatively,
change the source modules to export named symbols—whichever you choose, ensure
imports reference default exports (or make corresponding named exports) so
runtime "does not provide an export named" errors are resolved.

In @.github/scripts/helpers/checks.js:
- Around line 118-123: The current logic in the mergeability check treats an
unresolved mergeable state as “no conflicts” (logger.log at the
mergeableResolved branch) which can incorrectly pass PRs; change the behavior in
the function containing mergeableResolved/conflicts so that if mergeableResolved
is false you either set conflicts = true or throw an error instead of assuming
no conflicts, update the logger to reflect the fail-closed decision, and ensure
the returned object from the function (the return { passed: !conflicts } line)
will return passed: false (or never return because you throw) when mergeability
never resolved so the orchestrator will mark it as an error.

In @.github/scripts/helpers/config-loader.js:
- Around line 10-18: DEFAULT_CONFIG_PATH currently uses __dirname in an ESM
module (config-loader.js) which is undefined; replace the __dirname usage by
deriving the directory from import.meta.url (use fileURLToPath + path.dirname
from the 'url' module) and then compute DEFAULT_CONFIG_PATH with that directory;
update the top of the module to import or require fileURLToPath and derive
__dirname-equivalent before defining DEFAULT_CONFIG_PATH so functions/exports
that reference DEFAULT_CONFIG_PATH (the constant DEFAULT_CONFIG_PATH) no longer
throw at startup.

In @.github/workflows/on-pr-update.yml:
- Line 20: Add an actionlint configuration entry to whitelist the self-hosted
runner label used in .github/workflows/on-pr-update.yml (the runs-on value
"hiero-client-sdk-linux-large") so actionlint recognizes it as an intended
self-hosted label; update or create the actionlint config (e.g.,
.github/actionlint.yml or .actionlint.yml) to include
"hiero-client-sdk-linux-large" under allowed runner labels and also add a short
note in CI/runner documentation referencing that label for future maintenance.

In @.github/workflows/on-pr.yml:
- Around line 35-36: The checkout step currently uses actions/checkout without
disabling credential persistence; update the Checkout Repository step (the
actions/checkout@... usage) in both .github/workflows/on-pr.yml and
.github/workflows/on-pr-update.yml to include persist-credentials: false so the
checkout action does not persist the GITHUB_TOKEN across subsequent steps in the
privileged pull_request_target workflow.
- Around line 42-43: Replace the CommonJS require usage that loads the ESM bot
entrypoint (the line using require('./.github/scripts/bot-on-pr-open.js') and
the subsequent await script({ github, context })) with a dynamic import and
invoke the module's default export: use await
import('./.github/scripts/bot-on-pr-open.js') to get the module object and then
call its default export with the same arguments (e.g., await mod.default({
github, context })), ensuring the ESM export default is executed correctly.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9350694-07be-410d-b4e4-3a5293b59bcf

📥 Commits

Reviewing files that changed from the base of the PR and between 18474a1 and a616ca3.

📒 Files selected for processing (13)
  • .github/hiero-automation.json
  • .github/scripts/bot-on-pr-open.js
  • .github/scripts/bot-on-pr-update.js
  • .github/scripts/helpers/api.js
  • .github/scripts/helpers/checks.js
  • .github/scripts/helpers/comments.js
  • .github/scripts/helpers/config-loader.js
  • .github/scripts/helpers/constants.js
  • .github/scripts/helpers/index.js
  • .github/scripts/helpers/logger.js
  • .github/scripts/helpers/validation.js
  • .github/workflows/on-pr-update.yml
  • .github/workflows/on-pr.yml

}

const { allPassed } = await runAllChecksAndComment(botContext);
const result = await swapStatusLabel(botContext, allPassed);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Force status-label reconciliation on update events.

Line 35 calls swapStatusLabel without force, so PRs missing both status labels won’t recover a status label on synchronize/edited events.

Suggested fix
-    const result = await swapStatusLabel(botContext, allPassed);
+    const result = await swapStatusLabel(botContext, allPassed, { force: true });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const result = await swapStatusLabel(botContext, allPassed);
const result = await swapStatusLabel(botContext, allPassed, { force: true });
🤖 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 @.github/scripts/bot-on-pr-update.js at line 35, The PR update handler calls
swapStatusLabel(botContext, allPassed) without forcing reconciliation, so PRs
that lack both status labels won't get fixed on synchronize/edited events;
update the call in .github/scripts/bot-on-pr-update.js to pass the force flag
(e.g., swapStatusLabel(botContext, allPassed, true) or the equivalent third
argument expected by swapStatusLabel) so the function runs a forced
reconciliation of labels when handling update events.

Comment on lines +8 to +31
import { getLogger } from './logger.js';

import {
isSafeSearchToken,
requireObject,
requireNonEmptyString,
requirePositiveInt,
requireSafeUsername,
} from './validation.js';

import {
LABELS,
SKILL_HIERARCHY,
ISSUE_STATE,
} from './constants.js';

import {
checkDCO,
checkGPG,
checkMergeConflict,
checkIssueLink,
} from './checks.js';

import { buildBotComment } from './comments.js';

@coderabbitai coderabbitai Bot Jun 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Named imports in api.js =="
rg -n "import\\s*\\{[^}]+\\}\\s*from\\s*'\\./(logger|validation|constants|comments)\\.js'" .github/scripts/helpers/api.js

echo "== Export forms in referenced modules =="
for f in .github/scripts/helpers/logger.js \
         .github/scripts/helpers/validation.js \
         .github/scripts/helpers/constants.js \
         .github/scripts/helpers/comments.js \
         .github/scripts/helpers/config-loader.js; do
  echo "--- $f"
  rg -n "^export\\s+" "$f"
done

echo "== constants.js import from config-loader =="
rg -n "from './config-loader\\.js'" .github/scripts/helpers/constants.js

Repository: hiero-ledger/hiero-website

Length of output: 674


Fix default-vs-named import contracts before runtime (api.js + constants.js)

  • .github/scripts/helpers/api.js uses named imports from ./logger.js, ./validation.js, ./constants.js, and ./comments.js, but each of those modules exports only default { ... } objects—so module-link errors (“does not provide an export named …”) will occur.
  • .github/scripts/helpers/constants.js also imports named exports from ./config-loader.js, while config-loader.js is default-only, which would fail similarly once the api.js imports are corrected.
💡 One consistent fix path (import defaults + destructure)
-import { getLogger } from './logger.js';
+import loggerHelpers from './logger.js';
+const { getLogger } = loggerHelpers;

-import {
+import validationHelpers from './validation.js';
+const {
   isSafeSearchToken,
   requireObject,
   requireNonEmptyString,
   requirePositiveInt,
   requireSafeUsername,
-} from './validation.js';
+} = validationHelpers;

-import {
+import constants from './constants.js';
+const {
   LABELS,
   SKILL_HIERARCHY,
   ISSUE_STATE,
-} from './constants.js';
+} = constants;

-import { buildBotComment } from './comments.js';
+import commentHelpers from './comments.js';
+const { buildBotComment } = commentHelpers;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { getLogger } from './logger.js';
import {
isSafeSearchToken,
requireObject,
requireNonEmptyString,
requirePositiveInt,
requireSafeUsername,
} from './validation.js';
import {
LABELS,
SKILL_HIERARCHY,
ISSUE_STATE,
} from './constants.js';
import {
checkDCO,
checkGPG,
checkMergeConflict,
checkIssueLink,
} from './checks.js';
import { buildBotComment } from './comments.js';
import loggerHelpers from './logger.js';
const { getLogger } = loggerHelpers;
import validationHelpers from './validation.js';
const {
isSafeSearchToken,
requireObject,
requireNonEmptyString,
requirePositiveInt,
requireSafeUsername,
} = validationHelpers;
import constants from './constants.js';
const {
LABELS,
SKILL_HIERARCHY,
ISSUE_STATE,
} = constants;
import {
checkDCO,
checkGPG,
checkMergeConflict,
checkIssueLink,
} from './checks.js';
import commentHelpers from './comments.js';
const { buildBotComment } = commentHelpers;
🤖 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 @.github/scripts/helpers/api.js around lines 8 - 31, The imports are using
named imports but the target modules export only default objects; update the
import contracts to import the default and then destructure the needed symbols
(e.g., in .github/scripts/helpers/api.js import the default from './logger.js',
'./validation.js', './constants.js', and './comments.js' and then destructure
getLogger, isSafeSearchToken, requireObject, LABELS, SKILL_HIERARCHY,
ISSUE_STATE, buildBotComment, etc.), and likewise change
.github/scripts/helpers/constants.js to import the default from
'./config-loader.js' and destructure the required config values; alternatively,
change the source modules to export named symbols—whichever you choose, ensure
imports reference default exports (or make corresponding named exports) so
runtime "does not provide an export named" errors are resolved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about this suggestion?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Export lines in each helper module ==="
for f in .github/scripts/helpers/logger.js \
         .github/scripts/helpers/validation.js \
         .github/scripts/helpers/constants.js \
         .github/scripts/helpers/comments.js \
         .github/scripts/helpers/checks.js \
         .github/scripts/helpers/config-loader.js; do
  echo "--- $f"
  grep -n "^export" "$f" 2>/dev/null || echo "(no top-level exports found)"
done

echo ""
echo "=== Named imports in api.js (lines 8-31) ==="
sed -n '1,35p' .github/scripts/helpers/api.js

echo ""
echo "=== Named imports in constants.js ==="
grep -n "^import" .github/scripts/helpers/constants.js

Length of output: 2169



@danielmarv The suggestion is correct and must be fixed — the script confirms every helper module uses only export default { ... }, making all named imports in api.js (and constants.js) broken. They will throw at module link time with something like:

SyntaxError: The requested module './logger.js' does not provide an export named 'getLogger'

This affects:

File Broken named imports Source module's actual export
api.js getLogger, isSafeSearchToken, requireObject, … export default { at line 50/89/79/207/238
api.js checkDCO, checkGPG, checkMergeConflict, checkIssueLink export default { at line 238 in checks.js
constants.js loadAutomationConfig, buildConstants export default { at line 364 in config-loader.js

Note the original suggestion also missed the checks.js named imports in api.js (lines 24–29) — those need the same fix.


Two consistent ways to fix this:

Option A — Import default + destructure (minimal change, as suggested):

import loggerHelpers from './logger.js';
const { getLogger } = loggerHelpers;

import validationHelpers from './validation.js';
const { isSafeSearchToken, requireObject, requireNonEmptyString, requirePositiveInt, requireSafeUsername } = validationHelpers;

import constants from './constants.js';
const { LABELS, SKILL_HIERARCHY, ISSUE_STATE } = constants;

import checksHelpers from './checks.js';
const { checkDCO, checkGPG, checkMergeConflict, checkIssueLink } = checksHelpers;

import commentHelpers from './comments.js';
const { buildBotComment } = commentHelpers;

And in constants.js:

import configLoader from './config-loader.js';
const { loadAutomationConfig, buildConstants } = configLoader;

Option B — Switch source modules to named exports (more idiomatic ESM, enables tree-shaking):

Change each helper file from:

export default { getLogger, createLogger, createDelegatingLogger }

to:

export { getLogger, createLogger, createDelegatingLogger }

…applied consistently across all six helper files. This lets the current import statements in api.js and constants.js work as-is.

Recommendation: Option B is cleaner long-term — it's standard ESM practice and the index.js aggregator can still do import * as logger from './logger.js' or import named symbols directly. Option A works but creates an awkward two-step pattern throughout.

Comment on lines +670 to +673
const { marker, body, allPassed } = buildBotComment({ prAuthor, dco, gpg, merge, issueLink });
await postOrUpdateComment(botContext, marker, body);

return { allPassed };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t ignore dashboard upsert failures in the main PR-check flow.

runAllChecksAndComment awaits postOrUpdateComment but discards its failure result, so status-label transitions can proceed while the dashboard comment was not updated.

💡 Proposed fix
   const prAuthor = botContext.pr?.user?.login;
   const { marker, body, allPassed } = buildBotComment({ prAuthor, dco, gpg, merge, issueLink });
-  await postOrUpdateComment(botContext, marker, body);
+  const commentResult = await postOrUpdateComment(botContext, marker, body);
+  if (!commentResult.success) {
+    throw new Error(`Failed to upsert PR dashboard comment: ${commentResult.error}`);
+  }

   return { allPassed };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { marker, body, allPassed } = buildBotComment({ prAuthor, dco, gpg, merge, issueLink });
await postOrUpdateComment(botContext, marker, body);
return { allPassed };
const { marker, body, allPassed } = buildBotComment({ prAuthor, dco, gpg, merge, issueLink });
const commentResult = await postOrUpdateComment(botContext, marker, body);
if (!commentResult.success) {
throw new Error(`Failed to upsert PR dashboard comment: ${commentResult.error}`);
}
return { allPassed };
🤖 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 @.github/scripts/helpers/api.js around lines 670 - 673,
runAllChecksAndComment currently awaits postOrUpdateComment but ignores its
success/failure so dashboard upsert failures are dropped; change
runAllChecksAndComment to capture the result/error from postOrUpdateComment
(call to postOrUpdateComment(botContext, marker, body)), verify its returned
success flag or catch and rethrow/provide a failure indicator, and propagate
that failure in the function return (e.g., include an upsertSuccess/upsertError
field or throw) instead of unconditionally returning { allPassed } so
status-label transitions cannot proceed when the dashboard comment update
failed.

Comment on lines +118 to +123
if (!mergeableResolved) {
logger.log('Merge conflict check: mergeable never resolved after retries, assuming no conflicts');
}
logger.log(`Merge conflict check: ${conflicts ? 'has conflicts' : 'no conflicts'}`);
return { passed: !conflicts };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail-open mergeability handling can incorrectly pass PRs.

Line 118 treats unresolved mergeable as “no conflicts,” and Line 122 returns passed: true. If GitHub never resolves mergeability in time, this can incorrectly advance readiness. Fail closed (or throw so orchestrator marks it as an error) instead.

Suggested fix
-  if (!mergeableResolved) {
-    logger.log('Merge conflict check: mergeable never resolved after retries, assuming no conflicts');
-  }
-  logger.log(`Merge conflict check: ${conflicts ? 'has conflicts' : 'no conflicts'}`);
-  return { passed: !conflicts };
+  if (!mergeableResolved) {
+    throw new Error('Merge conflict check: mergeable state unresolved after retries');
+  }
+  logger.log(`Merge conflict check: ${conflicts ? 'has conflicts' : 'no conflicts'}`);
+  return { passed: !conflicts };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!mergeableResolved) {
logger.log('Merge conflict check: mergeable never resolved after retries, assuming no conflicts');
}
logger.log(`Merge conflict check: ${conflicts ? 'has conflicts' : 'no conflicts'}`);
return { passed: !conflicts };
}
if (!mergeableResolved) {
throw new Error('Merge conflict check: mergeable state unresolved after retries');
}
logger.log(`Merge conflict check: ${conflicts ? 'has conflicts' : 'no conflicts'}`);
return { passed: !conflicts };
}
🤖 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 @.github/scripts/helpers/checks.js around lines 118 - 123, The current logic
in the mergeability check treats an unresolved mergeable state as “no conflicts”
(logger.log at the mergeableResolved branch) which can incorrectly pass PRs;
change the behavior in the function containing mergeableResolved/conflicts so
that if mergeableResolved is false you either set conflicts = true or throw an
error instead of assuming no conflicts, update the logger to reflect the
fail-closed decision, and ensure the returned object from the function (the
return { passed: !conflicts } line) will return passed: false (or never return
because you throw) when mergeability never resolved so the orchestrator will
mark it as an error.

Comment on lines +10 to +18
import fs from 'fs';
import path from 'path';

/**
* Default path to the repository automation config file.
* Resolves from helpers/ → scripts/ → .github/hiero-automation.json.
* @type {string}
*/
const DEFAULT_CONFIG_PATH = path.resolve(__dirname, '../../hiero-automation.json');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Module type declarations =="
fd -HI --exact-depth 4 package.json | while read -r f; do
  echo "--- $f"
  jq -r '.type // "<no type field>"' "$f" 2>/dev/null || true
done

echo "== Workflow invocation context =="
rg -n --iglob ".github/workflows/*.yml" "bot-on-pr-open\\.js|bot-on-pr-update\\.js|github-script|node "

echo "== ESM/CJS path globals usage =="
rg -n "\\b__dirname\\b|import\\.meta\\.url" .github/scripts/helpers/config-loader.js .github/scripts/*.js

Repository: hiero-ledger/hiero-website

Length of output: 302


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Show config-loader.js lines 1-80 =="
CONFIG_FILE=".github/scripts/helpers/config-loader.js"
if [ -f "$CONFIG_FILE" ]; then
  nl -ba "$CONFIG_FILE" | sed -n '1,120p'
else
  echo "Missing: $CONFIG_FILE" >&2
fi

echo
echo "== Read root package.json type field =="
if [ -f "package.json" ]; then
  jq -r '"type=" + (.type // "null")' package.json 2>/dev/null || cat package.json | head
else
  echo "Missing: package.json" >&2
fi

echo
echo "== Find workflow files under .github/workflows (yaml/yml) =="
ls -la .github/workflows || true
fd -t f .github/workflows --extension yml --extension yaml || true
echo
echo "== Search workflows for bot-on-pr-* scripts =="
rg -n --hidden ".github/workflows/.*\\.(ya?ml)$" "bot-on-pr-open\\.js|bot-on-pr-update\\.js|actions/github-script" .github/workflows || true

echo
echo "== Search for __dirname/import.meta.url usage in .github/scripts =="
rg -n "\\b__dirname\\b|import\\.meta\\.url" .github/scripts || true

echo
echo "== Inspect bot entry scripts module syntax =="
for f in .github/scripts/bot-on-pr-open.js .github/scripts/bot-on-pr-update.js; do
  if [ -f "$f" ]; then
    echo "--- $f"
    nl -ba "$f" | sed -n '1,160p'
  else
    echo "Missing: $f"
  fi
done

Repository: hiero-ledger/hiero-website

Length of output: 170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

CONFIG_FILE=".github/scripts/helpers/config-loader.js"
if [ -f "$CONFIG_FILE" ]; then
  echo "== Show $CONFIG_FILE (lines 1-120) =="
  awk 'NR>=1 && NR<=120 {printf "%6d  %s\n", NR, $0}' "$CONFIG_FILE"
else
  echo "Missing: $CONFIG_FILE" >&2
fi

echo
echo "== Read root package.json type field =="
if [ -f "package.json" ]; then
  if command -v jq >/dev/null 2>&1; then
    jq -r '"type=" + (.type // "null")' package.json 2>/dev/null || true
  fi
  echo "--- snippet ---"
  grep -n '"type"' package.json | head -n 20 || true
else
  echo "Missing: package.json" >&2
fi

echo
echo "== List .github/workflows =="
ls -la .github/workflows 2>/dev/null || true
echo
echo "== Workflow search for bot scripts =="
rg -n --hidden --no-ignore ".github/workflows" "bot-on-pr-open\\.js|bot-on-pr-update\\.js|actions/github-script" || true

echo
echo "== Search __dirname/import.meta.url usage =="
rg -n --hidden --no-ignore "\\b__dirname\\b|import\\.meta\\.url" .github/scripts || true

echo
echo "== Inspect bot entry scripts =="
for f in .github/scripts/bot-on-pr-open.js .github/scripts/bot-on-pr-update.js; do
  if [ -f "$f" ]; then
    echo "--- $f (lines 1-200) ---"
    awk 'NR>=1 && NR<=200 {printf "%6d  %s\n", NR, $0}' "$f"
  else
    echo "Missing: $f"
  fi
done

Repository: hiero-ledger/hiero-website

Length of output: 11063


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search for config-loader imports =="
rg -n --hidden --no-ignore "config-loader" .github/scripts || true

echo
echo "== Read .github/scripts/helpers/helpers.js (if present) =="
HELPERS=".github/scripts/helpers/helpers.js"
if [ -f "$HELPERS" ]; then
  awk 'NR>=1 && NR<=260 {printf "%6d  %s\n", NR, $0}' "$HELPERS"
else
  echo "Missing: $HELPERS"
fi

echo
echo "== Inspect workflows for how bot scripts are executed =="
ls -la .github/workflows
for y in .github/workflows/*.yml .github/workflows/*.yaml; do
  [ -e "$y" ] || continue
  echo "--- $y (lines 1-220) ---"
  awk 'NR>=1 && NR<=220 {printf "%6d  %s\n", NR, $0}' "$y"
done

Repository: hiero-ledger/hiero-website

Length of output: 7829


Fix ESM __dirname usage in .github/scripts/helpers/config-loader.js to prevent startup-time crashes.

Root package.json sets "type": "module", and config-loader.js is an ESM module (import ...), so const DEFAULT_CONFIG_PATH = path.resolve(__dirname, ...) at line 18 will throw (__dirname is not defined) and block config loading.

💡 Proposed fix
 import fs from 'fs';
 import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);

 /**
  * Default path to the repository automation config file.
  * Resolves from helpers/ → scripts/ → .github/hiero-automation.json.
  * `@type` {string}
  */
 const DEFAULT_CONFIG_PATH = path.resolve(__dirname, '../../hiero-automation.json');
🤖 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 @.github/scripts/helpers/config-loader.js around lines 10 - 18,
DEFAULT_CONFIG_PATH currently uses __dirname in an ESM module (config-loader.js)
which is undefined; replace the __dirname usage by deriving the directory from
import.meta.url (use fileURLToPath + path.dirname from the 'url' module) and
then compute DEFAULT_CONFIG_PATH with that directory; update the top of the
module to import or require fileURLToPath and derive __dirname-equivalent before
defining DEFAULT_CONFIG_PATH so functions/exports that reference
DEFAULT_CONFIG_PATH (the constant DEFAULT_CONFIG_PATH) no longer throw at
startup.


jobs:
on-pr-update:
runs-on: hiero-client-sdk-linux-large

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether actionlint config exists and how runner labels are declared
fd -HI "actionlint.yaml" .github
fd -HI "actionlint.yml" .github
rg -n "runs-on|hiero-client-sdk-linux-large|self-hosted" .github/workflows/on-pr-update.yml

Repository: hiero-ledger/hiero-website

Length of output: 116


Clarify/whitelist the runner label hiero-client-sdk-linux-large for CI linting.
.github/workflows/on-pr-update.yml uses runs-on: hiero-client-sdk-linux-large, but there’s no actionlint config in .github/ to document/whitelist runner labels—so CI linting can’t reliably resolve self-hosted intent. Ensure actionlint configuration (or equivalent lint rules) and any runner documentation reflect this label.

🧰 Tools
🪛 actionlint (1.7.12)

[error] 20-20: label "hiero-client-sdk-linux-large" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2025-vs2026", "windows-2022", "windows-11-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-intel", "macos-26-xlarge", "macos-26-large", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🤖 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 @.github/workflows/on-pr-update.yml at line 20, Add an actionlint
configuration entry to whitelist the self-hosted runner label used in
.github/workflows/on-pr-update.yml (the runs-on value
"hiero-client-sdk-linux-large") so actionlint recognizes it as an intended
self-hosted label; update or create the actionlint config (e.g.,
.github/actionlint.yml or .actionlint.yml) to include
"hiero-client-sdk-linux-large" under allowed runner labels and also add a short
note in CI/runner documentation referencing that label for future maintenance.

Source: Linters/SAST tools

Comment on lines +35 to +36
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Common root cause: credential persistence enabled in privileged pull_request_target workflows (.github/workflows/on-pr.yml, .github/workflows/on-pr-update.yml).
actions/checkout should set persist-credentials: false in both files to reduce write-token exposure across steps.

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 35-36: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 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 @.github/workflows/on-pr.yml around lines 35 - 36, The checkout step
currently uses actions/checkout without disabling credential persistence; update
the Checkout Repository step (the actions/checkout@... usage) in both
.github/workflows/on-pr.yml and .github/workflows/on-pr-update.yml to include
persist-credentials: false so the checkout action does not persist the
GITHUB_TOKEN across subsequent steps in the privileged pull_request_target
workflow.

Source: Linters/SAST tools

Comment on lines +42 to +43
const script = require('./.github/scripts/bot-on-pr-open.js');
await script({ github, context });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate workflow files
ls -la .github/workflows | sed -n '1,200p'

# Show relevant snippets for on-pr.yml around lines 35-60
echo "---- on-pr.yml (lines 30-70) ----"
nl -ba .github/workflows/on-pr.yml | sed -n '30,80p'

# Show relevant snippets for on-pr-update.yml around lines 30-80 if exists
if [ -f .github/workflows/on-pr-update.yml ]; then
  echo "---- on-pr-update.yml (lines 30-90) ----"
  nl -ba .github/workflows/on-pr-update.yml | sed -n '30,110p'
else
  echo "Missing .github/workflows/on-pr-update.yml"
fi

# List bot scripts referenced
echo "---- bot scripts in .github/scripts (filtered) ----"
ls -ლა .github/scripts | sed -n '1,200p' || true

for f in .github/scripts/bot-on-pr-open.js .github/scripts/bot-on-pr-update.js; do
  if [ -f "$f" ]; then
    echo "---- $f (head 120) ----"
    nl -ba "$f" | sed -n '1,140p'
  else
    echo "Missing $f"
  fi
done

# Check package.json type/module for ESM resolution
if [ -f package.json ]; then
  echo "---- package.json (type field) ----"
  node -p "JSON.parse(require('fs').readFileSync('package.json','utf8')).type || 'none'" 2>/dev/null || true
  cat package.json | sed -n '1,120p'
else
  echo "No package.json found"
fi

Repository: hiero-ledger/hiero-website

Length of output: 526


🏁 Script executed:

#!/bin/bash
set -euo pipefail

show_with_lines () {
  local file="$1"
  local from="$2"
  local to="$3"
  python3 - <<PY
import itertools,sys
path=sys.argv[1]
a=int(sys.argv[2]); b=int(sys.argv[3])
with open(path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f, start=1):
        if a <= i <= b:
            sys.stdout.write(f"{i:5d}: {line}")
PY
  "$file" "$from" "$to"
}

echo "---- workflow files ----"
ls -ლა .github/workflows

echo "---- on-pr.yml (lines 35-90) ----"
show_with_lines ".github/workflows/on-pr.yml" 35 90

echo "---- on-pr-update.yml (lines 35-120) ----"
show_with_lines ".github/workflows/on-pr-update.yml" 35 120

echo "---- grep require usage in those workflows ----"
rg -n "require\\(\\s*['\"]\\.\\/\\.github\\/scripts\\/bot-on-pr-" .github/workflows/on-pr.yml .github/workflows/on-pr-update.yml || true
rg -n "require\\(" .github/workflows/on-pr.yml .github/workflows/on-pr-update.yml || true

echo "---- bot scripts content (first 200 lines each) ----"
for f in .github/scripts/bot-on-pr-open.js .github/scripts/bot-on-pr-update.js; do
  if [ -f "$f" ]; then
    echo "===== $f ====="
    show_with_lines "$f" 1 220
  else
    echo "Missing $f"
  fi
done

echo "---- detect module format in bot scripts ----"
for f in .github/scripts/bot-on-pr-open.js .github/scripts/bot-on-pr-update.js; do
  if [ -f "$f" ]; then
    echo "=== $f: export/module.exports/require presence ==="
    rg -n "(^|\\s)export\\s|export\\s+default|module\\.exports\\s*=|module\\.exports\\s*\\.|require\\s*\\(" "$f" || true
  fi
done

echo "---- package.json (type field) ----"
if [ -f package.json ]; then
  node -e "const fs=require('fs'); const p=JSON.parse(fs.readFileSync('package.json','utf8')); console.log(p.type||'none')" || true
  sed -n '1,120p' package.json
else
  echo "No package.json found"
fi

Repository: hiero-ledger/hiero-website

Length of output: 177


🏁 Script executed:

#!/bin/bash
set -euo pipefail

show_with_lines () {
  python3 - "$1" "$2" "$3" <<'PY'
import sys
path, a, b = sys.argv[1], int(sys.argv[2]), int(sys.argv[3])
with open(path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f, start=1):
        if a <= i <= b:
            sys.stdout.write(f"{i:5d}: {line}")
PY
}

echo "---- workflow files ----"
ls -la .github/workflows || true

echo "---- on-pr.yml (lines 1-140) ----"
show_with_lines ".github/workflows/on-pr.yml" 1 140

echo "---- on-pr-update.yml (lines 1-200) ----"
show_with_lines ".github/workflows/on-pr-update.yml" 1 200

echo "---- grep require usage in workflows ----"
rg -n "require\\(" .github/workflows/on-pr.yml .github/workflows/on-pr-update.yml || true

echo "---- bot scripts existence ----"
ls -la .github/scripts || true

for f in .github/scripts/bot-on-pr-open.js .github/scripts/bot-on-pr-update.js; do
  echo "---- $f (1-220) ----"
  if [ -f "$f" ]; then
    show_with_lines "$f" 1 220
    echo "---- module format signals in $f ----"
    rg -n "(^|\\s)export\\s+default|(^|\\s)export\\s+\\{|module\\.exports\\s*=|module\\.exports\\s*\\.|require\\s*\\(" "$f" || true
  else
    echo "Missing $f"
  fi
done

echo "---- package.json (type) ----"
if [ -f package.json ]; then
  node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));console.log('type:',p.type||'none')" || true
  sed -n '1,120p' package.json
else
  echo "No package.json found"
fi

Repository: hiero-ledger/hiero-website

Length of output: 10626


Fix ESM bot entrypoint loading in actions/github-script (replace require with import + .default)
.github/workflows/on-pr.yml and .github/workflows/on-pr-update.yml use require('./.github/scripts/bot-on-pr-*.js'), but the bot entrypoints are ESM (package.json has "type": "module" and both scripts use export default). Switch to dynamic import() and invoke the default export:

const mod = await import('./.github/scripts/bot-on-pr-open.js'); // or bot-on-pr-update.js
await mod.default({ github, context });
🤖 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 @.github/workflows/on-pr.yml around lines 42 - 43, Replace the CommonJS
require usage that loads the ESM bot entrypoint (the line using
require('./.github/scripts/bot-on-pr-open.js') and the subsequent await script({
github, context })) with a dynamic import and invoke the module's default
export: use await import('./.github/scripts/bot-on-pr-open.js') to get the
module object and then call its default export with the same arguments (e.g.,
await mod.default({ github, context })), ensuring the ESM export default is
executed correctly.

@danielmarv danielmarv added the update branch Contributor needs to click update branch label Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a PR Dashboard

3 participants