diff --git a/.github/workflows/runner-hermes-native.yml b/.github/workflows/runner-hermes-native.yml index c9067f98fa3..0eca7990d31 100644 --- a/.github/workflows/runner-hermes-native.yml +++ b/.github/workflows/runner-hermes-native.yml @@ -46,6 +46,17 @@ jobs: with: version: 9.15.4 + - name: Qualify the job's provider Node interpreter + run: | + node <<'NODE' + const fs = require('node:fs'); + const mode = fs.statSync(process.execPath).mode & 0o777; + fs.chmodSync(process.execPath, mode & ~0o022); + if ((fs.statSync(process.execPath).mode & 0o022) !== 0) { + throw new Error('provider Node interpreter remains group- or world-writable'); + } + NODE + - name: Install workspace dependencies run: | set -euo pipefail diff --git a/doc/plans/2026-10-06-hermes-native-runner.md b/doc/plans/2026-10-06-hermes-native-runner.md index 3285366057a..2c8c665fc23 100644 --- a/doc/plans/2026-10-06-hermes-native-runner.md +++ b/doc/plans/2026-10-06-hermes-native-runner.md @@ -403,6 +403,18 @@ excluding private native homes. Its actual execution result must be checked; the workflow declaration alone is not Linux proof. The protected paid workflow and its default-branch authorization remain unchanged by this addition. +The first Linux CI attempt provisioned the exact pinned closure and passed the +native ACPX fixture in 94.21 seconds, including native command tools, planning, +images, questions, controls, memory and strict recovery. The Rust fixture failed +before native launch because GitHub's Node interpreter was group-writable. +The workflow now removes only group/world write bits from its own interpreter, +matching the existing paid workflow's setup step. The launch verifier is +unchanged; both native fixtures must pass on a new attempt. The corresponding +ordinary PR run recorded a timeout in the unchanged chat retry denial-feedback +browser test, followed by simultaneous runner shutdowns across other jobs. +Their failures remain recorded and need fresh CI. Repository-wide typecheck +and build pass on startup runtime head `2cf45fa7ff52b08215c726cf10b7d8353e69892a`. + The three draft PRs had green CI and fresh Greptile 5/5 at routines `3745f3c5a46bda7778ee132682d1b7ae23b088c1`, native `0dea682031f8e35631faee7a05519ed4dce80d07`, and qualification diff --git a/tests/runner-e2e/workflow-security.test.ts b/tests/runner-e2e/workflow-security.test.ts index 9dd30ccbc90..2ae63104942 100644 --- a/tests/runner-e2e/workflow-security.test.ts +++ b/tests/runner-e2e/workflow-security.test.ts @@ -27,6 +27,8 @@ describe("public repository paid workflow security", () => { expect(fixtures).toContain("PAPERCLIP_HERMES_QUALIFY=1"); expect(fixtures).not.toMatch(/(?:API_KEY|TOKEN|AUTH_JSON|KEEP)/); expect(workflow.indexOf("Provision the pinned Python closure")).toBeLessThan(workflow.indexOf("Run native fixtures")); + expect(workflow.indexOf("Qualify the job's provider Node interpreter")).toBeLessThan(workflow.indexOf("Run native fixtures")); + expect(workflow).toContain("fs.chmodSync(process.execPath, mode & ~0o022)"); expect(workflow).toContain("no paid model, browser or Daytona proof"); const upload = workflow.slice(workflow.indexOf(" - name: Upload fixture evidence")); expect(upload).toContain("hermes-native-evidence/");