ci: fix Node-version drift, add composite-action schema validation#7408
Merged
Conversation
Two independent hygiene fixes from the follow-up audit, bundled since both are small and neither conflicts with the other. Node-version drift: 13 release/publish-adjacent workflows hardcoded node-version: 24.18.0 while .nvmrc pins 22 -- every PR-facing workflow (ci.yml, ui-preview.yml, ui-deploy.yml, ui-sentry-release.yml, mcp-release-candidate.yml) already correctly used node-version-file: .nvmrc. Investigated before touching anything: none of the published packages' own engines fields (@loopover/engine, mcp, ui-kit all >=22.0.0; miner >=22.13.0) require Node 24, no comment anywhere explains the choice, and git history shows each was just whatever version was current when that particular publish workflow was first scaffolded, never reconciled against .nvmrc. Switched all 13 (18 occurrences) to node-version-file: .nvmrc for consistency. Left the two files already pinned to 22.23.1 alone (gittensor-impact.yml, visual-capture-fallback.yml) -- same major version as .nvmrc, not actually inconsistent, lower value/higher risk to touch than the real Node-24-vs-22 mismatch. Composite-action schema validation: this repo's actionlint (both the npm wrapper and the raw upstream binary, tested directly) does not support .github/actions/**/action.yml files at all -- confirmed a genuine, long-standing limitation (rhysd/actionlint#46 and #401, open since 2021), not a configuration gap. Every file passed to actionlint is parsed as a workflow regardless of shape, so it errors on runs/inputs/outputs as unexpected top-level keys rather than actually validating the composite action. scripts/lint-composite-actions.mjs is the closest available substitute: validates against GitHub's own official action-metadata JSON Schema (vendored locally at scripts/schemas/github-action.schema.json, not fetched live, so this check doesn't depend on network access in CI), plus a dedicated check for one thing the schema alone doesn't catch -- every run: step in a composite action needs an explicit shell:, unlike a top-level workflow job which defaults to bash. Verified it actually catches real problems, not just that it exists: fed it a deliberately broken action file (missing shell:, an invalid uses: type) and confirmed both classes of error surface clearly. Wired into test:ci (npm run lint:composite-actions) and a new "Lint composite actions" step in ci.yml, alongside the existing "Lint workflows" step. Also added .github/actions/** to the backend path filter -- it was missing entirely (only .github/workflows/** was covered), so a PR editing ONLY a composite action file would previously trigger neither this new check nor the existing actionlint step at all.
Contributor
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | cc55d8c | Commit Preview URL Branch Preview URL |
Jul 20 2026, 07:19 AM |
Bundle ReportBundle size has no change ✅ |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7408 +/- ##
=======================================
Coverage 91.21% 91.21%
=======================================
Files 716 716
Lines 72789 72789
Branches 20859 20859
=======================================
+ Hits 66392 66393 +1
+ Misses 5355 5354 -1
Partials 1042 1042
Flags with carried forward coverage won't be shown. Click here to find out more. |
12 tasks
JSONbored
added a commit
that referenced
this pull request
Jul 21, 2026
… .nvmrc node-version-file: .nvmrc (introduced repo-wide by 8bd60f0 / #7408) broke this one job: it deliberately has no actions/checkout step (see the file header -- it never runs fork/PR code), so .nvmrc doesn't exist on the runner and Setup Node has failed on every run since. That failure trips the "Record FAILED deployment for Reviewbot" step, which writes a permanent failure GitHub Deployment status -- so ORB's preview-URL discovery (preview-url.ts's getLatestDeploymentStatus) short-circuits on that failure and never even checks the separately-successful Cloudflare Workers Builds deployment, breaking the "after" screenshot/scroll-preview for every visual PR repo-wide, including this one on PR #7581. Hardcodes node-version instead, matching the same already-established exception in gittensor-impact.yml and visual-capture-fallback.yml.
JSONbored
added a commit
that referenced
this pull request
Jul 21, 2026
… report, contributor trust profiles (#7581) * fix(review): repair the public decision-accuracy metric's reversal detection The reversal query derived "mistake" status from a PR's own state after a close/merge, which can never detect a merge undone by a separate revert PR (a merged PR's state never returns to open on GitHub) and was scoped only to a frozen own-ledger snapshot. Reuse the existing reversal_reopened/ reversal_reverted events already recorded elsewhere, and blend in live fleet-wide accuracy across registered self-hosted instances so the public number reflects the current fleet, not a historical snapshot. Also surfaces the fleet's anti-gaming detection count (#2350) on the same payload. Refs #7567 * feat(site): add a public fairness report page Adds /fairness, linked from the homepage's Decision accuracy tile: the full 8-week accuracy trend, per-repo breakdown, and anti-gaming-flags- caught count, plus a short methodology note. Reuses the existing /v1/public/stats payload rather than adding a new endpoint -- aggregate counts only, no PR content or contributor identities. Refs #7567 * feat(review): add internal contributor trust profiles Per-login, per-repo gate-decision accuracy (mirrors computeGateEval, keyed by contributor_gate_history instead of the actor-free review_audit) and fairness-outlier detection that flags deviation from a project's median in either direction, never asserting fault. Composes with per-repo submission counts and moderation-violation history into one trust profile per contributor, plus a backfill mechanism reconstructing historical gate history from review_audit + pull_requests.author_login for PRs that predate contributor_gate_history. Internal, bearer-gated routes only -- never exposed publicly. Config-as- code throughout: a global LOOPOVER_FAIRNESS_ANALYTICS flag overridable via the self-repo's .loopover.yml (mirroring publicStats/ops), plus a per-repo fairnessAnalyticsMode so any installed repo can opt its own gate decisions and moderation history out of the aggregation via its own private .loopover.yml. Refs #7567 * fix(site): guard against a fleetAccuracy-less /v1/public/stats response Discovered via manual verification: the homepage stats band and the new fairness report page both read data.fleetAccuracy without a null guard, which crashes (and is caught by the route error boundary) against any currently-live API response, since that field doesn't exist until the backend change ships. Add defensive optional chaining so both surfaces degrade to the own-ledger accuracy number instead, matching the intended fallback behavior once the fields genuinely diverge only during a frontend-ahead-of-backend deployment window. Refs #7567 * chore(openapi): regenerate spec after rebase onto main The rebase conflict resolution took main's openapi.json wholesale for one commit; regenerate from the final merged schemas so both this PR's and #7521's additions are present. * fix(review): sync fairnessAnalytics config docs into loopover.full.yml The per-repo fairnessAnalyticsMode setting and fleet-wide fairnessAnalytics block were only documented in .loopover.yml.example, drifting from config/examples/loopover.full.yml which must stay byte-identical from the canonical marker onward (test/unit/config-templates.test.ts). * fix(review): remove stray null byte from contributor-gate-eval.ts A literal NUL byte had been typed into the (login, project) map-key template literal instead of a separator character, making git and other tooling treat the whole file as binary (no diff, no line-level coverage attribution). Replaced with ':', matching the composite-key separator convention used elsewhere (rag.ts, inline-comments-select.ts). * fix(ci): pin Node version in ui-preview-deploy.yml instead of reading .nvmrc node-version-file: .nvmrc (introduced repo-wide by 8bd60f0 / #7408) broke this one job: it deliberately has no actions/checkout step (see the file header -- it never runs fork/PR code), so .nvmrc doesn't exist on the runner and Setup Node has failed on every run since. That failure trips the "Record FAILED deployment for Reviewbot" step, which writes a permanent failure GitHub Deployment status -- so ORB's preview-URL discovery (preview-url.ts's getLatestDeploymentStatus) short-circuits on that failure and never even checks the separately-successful Cloudflare Workers Builds deployment, breaking the "after" screenshot/scroll-preview for every visual PR repo-wide, including this one on PR #7581. Hardcodes node-version instead, matching the same already-established exception in gittensor-impact.yml and visual-capture-fallback.yml. * fix(review): fall back to a placeholder for a missing after-GIF The scroll-preview "after" slot resolved to undefined (rendered as a bare "—") whenever there was no live preview page to capture, unlike the screenshot slot which already substitutes a loading/failed placeholder (afterPlaceholder) in the same situation. That made "GIF capture still building/failed" indistinguishable from "GIF capture isn't configured for this repo at all". actions_fallback never produces a GIF (only static PNGs), so there's no cached fallback artifact to look up here the way resolveFallbackAfterShot does for the screenshot slot -- the placeholder substitution is the whole fix. * test(review): cover the missing-limit branch on the backfill route Closes the last codecov/patch gap on PR #7581 (routes.ts's typeof body?.limit === "number" ternary only had its truthy arm exercised).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent hygiene fixes from the follow-up audit, bundled since both are small and neither conflicts with the other.
Node-version drift. 13 release/publish-adjacent workflows hardcoded
node-version: 24.18.0while.nvmrcpins22— every PR-facing workflow (ci.yml,ui-preview.yml,ui-deploy.yml,ui-sentry-release.yml,mcp-release-candidate.yml) already correctly usednode-version-file: .nvmrc. Investigated before touching anything: none of the published packages' ownenginesfields (@loopover/engine,mcp,ui-kitall>=22.0.0;miner>=22.13.0) require Node 24, no comment anywhere explains the choice, and git history shows each was just whatever version was current when that particular publish workflow was first scaffolded, never reconciled against.nvmrc. Switched all 13 files (18 occurrences) tonode-version-file: .nvmrc. Left the two files already pinned to22.23.1alone (gittensor-impact.yml,visual-capture-fallback.yml) — same major version as.nvmrc, not actually inconsistent, lower value to touch than the real 24-vs-22 mismatch.Composite-action schema validation. This repo's
actionlint— both the npm wrapper and the raw upstream binary, tested directly with no wrapper — does not support.github/actions/**/action.ymlfiles at all. Confirmed a genuine, long-standing limitation (rhysd/actionlint#46 and #401, open since 2021), not a configuration gap: every file passed to actionlint is parsed as a workflow regardless of shape, so it errors onruns/inputs/outputsas unexpected top-level keys rather than validating the composite action.scripts/lint-composite-actions.mjsis the closest available substitute — validates against GitHub's own official action-metadata JSON Schema (vendored locally, not fetched live, so this doesn't add a network dependency to CI), plus a dedicated check for the one thing the schema alone doesn't catch: everyrun:step in a composite action needs an explicitshell:, unlike a top-level workflow job which defaults to bash. Verified it actually catches real problems, not just that it exists — fed it a deliberately broken action file (missingshell:, an invaliduses:type) and confirmed both surface clearly.Wired into
test:ciand a new "Lint composite actions" step inci.yml, alongside "Lint workflows". Also added.github/actions/**to thebackendpath filter — it was missing entirely (only.github/workflows/**was covered), so a PR editing only a composite action file would previously trigger neither the new check nor the existingactionlintstep at all.Scope
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm run lint:composite-actions(the new check itself, run against the real repo — clean)npm run typecheckenginesfield, searched for any explanatory comment, and checked git blame/history for the original introducing commit — found nothing justifying 24 over 22.action.yml(missingshell:, invaliduses:type) in a throwaway fixture and confirmed both error classes surface with clear messages.test:cicomposition, thebackendfilter, the composite-action setup, observability, codecov policy, predicted-gate) — 181/181 passing, confirming thetest:ciscript insertion and filter addition didn't break any exact-match assertions.npm run test:coverage— not run in full; this PR touches nosrc/**file.npm audit --audit-level=moderate—ajvadded as a new devDependency (already present transitively at two different versions; added directly and explicitly rather than relying on an unpinned transitive resolution). No known advisories for the version installed.If any required check was skipped, explain why: this PR is CI/build-tooling configuration — the skipped commands aren't applicable to source-only checks, and every actually-relevant command (including proving the new validator catches real bugs, not just that it exists) is listed above.
Safety
ajv) is a well-established, widely-used JSON Schema validator already present transitively in this repo's own dependency tree at two different versions — added explicitly for a pinned, predictable resolution rather than relying on whatever a futurenpm installhappens to resolve transitively.Notes
.github/workflows/**(a guarded path), so it'll be held for manual owner review rather than auto-merged.