fix(release): recover immutable patch validation gates (#16984)

* fix(release): recover immutable patch validation gates

* test(e2e): locate wrapped terminal file links

* test(e2e): keep sibling file links on one terminal row
This commit is contained in:
Jinwoo Hong
2026-08-28 00:55:45 -07:00
committed by GitHub
parent 4bb337741c
commit 59515beb70
7 changed files with 152 additions and 39 deletions
+31 -1
View File
@@ -844,6 +844,16 @@ jobs:
with:
ref: refs/tags/${{ needs.cut.outputs.tag }}
- name: Restore golden test harness from the workflow ref
shell: bash
env:
WORKFLOW_SHA: ${{ github.workflow_sha }}
run: |
git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA"
git checkout "$WORKFLOW_SHA" -- \
tests/e2e/golden-source-control-open-diff.spec.ts \
tests/e2e/golden-terminal-file-link.spec.ts
- name: Install native build tools
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y build-essential python3 xvfb
@@ -946,6 +956,16 @@ jobs:
with:
ref: refs/tags/${{ needs.cut.outputs.tag }}
- name: Restore skill-sharing test harness from the workflow ref
shell: bash
env:
WORKFLOW_SHA: ${{ github.workflow_sha }}
run: |
git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA"
git checkout "$WORKFLOW_SHA" -- \
src/main/skills/skill-freshness-inventory.test.ts \
src/main/skills/skill-provider-runtime-roots.test.ts
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
@@ -978,13 +998,23 @@ jobs:
steps:
- name: Install Ubuntu 20.04 prerequisites
run: apt-get update && apt-get install -y build-essential ca-certificates git python3
run: apt-get update && apt-get install -y build-essential ca-certificates git python3 unzip
- name: Checkout
uses: actions/checkout@v6
with:
ref: refs/tags/${{ needs.cut.outputs.tag }}
- name: Restore skill-sharing test harness from the workflow ref
shell: bash
env:
WORKFLOW_SHA: ${{ github.workflow_sha }}
run: |
git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA"
git checkout "$WORKFLOW_SHA" -- \
src/main/skills/skill-freshness-inventory.test.ts \
src/main/skills/skill-provider-runtime-roots.test.ts
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
@@ -10,6 +10,17 @@ const releaseWorkflow = parse(
const e2eWorkflow = parse(readFileSync(join(projectDir, '.github/workflows/e2e.yml'), 'utf8'))
describe('release E2E dispatch contract', () => {
it('validates immutable tags with the current golden test harness', () => {
const restoreStep = releaseWorkflow.jobs['terminal-rendering-golden'].steps.find(
(step) => step.name === 'Restore golden test harness from the workflow ref'
)
expect(restoreStep.env.WORKFLOW_SHA).toBe('${{ github.workflow_sha }}')
expect(restoreStep.run).toContain('git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA"')
expect(restoreStep.run).toContain('golden-source-control-open-diff.spec.ts')
expect(restoreStep.run).toContain('golden-terminal-file-link.spec.ts')
})
it('dispatches tag-scoped E2E only after publication', () => {
const dispatchJob = releaseWorkflow.jobs['post-release-e2e']
const dispatchStep = dispatchJob.steps.find((step) => step.name === 'Dispatch tag-scoped E2E')
@@ -44,6 +44,30 @@ describe('skill-sharing release workflow', () => {
expect(command).toContain('src/shared/skill-bundle-install-contract.test.ts')
})
it('installs the archive tool required by Electron on the Linux floor', () => {
const linux = workflow.jobs['skill-sharing-linux-floor-release-gate']
const prerequisites = stepNamed(linux, 'Install Ubuntu 20.04 prerequisites')
expect(prerequisites.run).toMatch(/apt-get install[^\n]*\bunzip\b/)
})
it('validates immutable tags with the current skill-sharing test harness', () => {
for (const jobName of [
'skill-sharing-release-gate',
'skill-sharing-linux-floor-release-gate'
]) {
const restore = stepNamed(
workflow.jobs[jobName],
'Restore skill-sharing test harness from the workflow ref'
)
expect(restore.env.WORKFLOW_SHA).toBe('${{ github.workflow_sha }}')
expect(restore.run).toContain('git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA"')
expect(restore.run).toContain('skill-freshness-inventory.test.ts')
expect(restore.run).toContain('skill-provider-runtime-roots.test.ts')
}
})
it('archives bounded machine-readable evidence from every platform', () => {
for (const jobName of [
'skill-sharing-release-gate',
@@ -157,7 +157,11 @@ async function writeSkillLockHash(homeDir: string, skillFolderHash: string): Pro
}
afterEach(async () => {
await Promise.all(temporaryDirectories.splice(0).map((root) => rm(root, { recursive: true })))
await Promise.all(
temporaryDirectories
.splice(0)
.map((root) => rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }))
)
})
describe('read-only skill freshness inventory', () => {
@@ -1,4 +1,4 @@
import { join } from 'node:path'
import { join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import {
resolveDefaultHermesSkillsRoot,
@@ -10,14 +10,16 @@ import {
describe('skill provider runtime roots', () => {
it('maps Claude and Grok config homes to their global skill roots', () => {
const claudeRoot = resolve('/srv/claude')
const grokRoot = resolve('/srv/grok')
expect(
resolveEnvironmentSkillProviderRoots({
CLAUDE_CONFIG_DIR: join('/srv', 'claude'),
GROK_HOME: join('/srv', 'grok')
CLAUDE_CONFIG_DIR: claudeRoot,
GROK_HOME: grokRoot
})
).toEqual({
claude: join('/srv', 'claude', 'skills'),
grok: join('/srv', 'grok', 'skills')
claude: join(claudeRoot, 'skills'),
grok: join(grokRoot, 'skills')
})
})
@@ -27,14 +29,16 @@ describe('skill provider runtime roots', () => {
GROK_HOME: '../grok'
})
expect(roots).toEqual({})
expect(withClaudeSkillProviderRoot(roots, join('/managed', 'claude'))).toEqual({
claude: join('/managed', 'claude', 'skills')
const managedClaudeRoot = resolve('/managed/claude')
expect(withClaudeSkillProviderRoot(roots, managedClaudeRoot)).toEqual({
claude: join(managedClaudeRoot, 'skills')
})
})
it('maps a relocated HERMES_HOME to its skill root and ignores relative ones', () => {
expect(resolveEnvironmentHermesSkillsRoot({ HERMES_HOME: join('/srv', 'hermes') })).toBe(
join('/srv', 'hermes', 'skills')
const hermesRoot = resolve('/srv/hermes')
expect(resolveEnvironmentHermesSkillsRoot({ HERMES_HOME: hermesRoot })).toBe(
join(hermesRoot, 'skills')
)
expect(resolveEnvironmentHermesSkillsRoot({ HERMES_HOME: '../hermes' })).toBeNull()
expect(resolveEnvironmentHermesSkillsRoot({})).toBeNull()
@@ -52,13 +56,14 @@ describe('skill provider runtime roots', () => {
})
it('defaults the Hermes skills root under LOCALAPPDATA on Windows', () => {
const localAppData = resolve('/local')
const resolved = resolveDefaultHermesSkillsRoot({
homeDir: join('/users', 'alice'),
platform: 'win32',
env: { LOCALAPPDATA: join('/local') },
directoryExists: (candidate) => candidate === join('/local', 'hermes')
env: { LOCALAPPDATA: localAppData },
directoryExists: (candidate) => candidate === join(localAppData, 'hermes')
})
expect(resolved).toBe(join('/local', 'hermes', 'skills'))
expect(resolved).toBe(join(localAppData, 'hermes', 'skills'))
})
it('keeps a pre-LOCALAPPDATA Windows dotfolder install discoverable', () => {
@@ -72,13 +77,14 @@ describe('skill provider runtime roots', () => {
})
it('prefers the LOCALAPPDATA tree on Windows when both layouts exist', () => {
const localAppData = resolve('/local')
const resolved = resolveDefaultHermesSkillsRoot({
homeDir: join('/users', 'alice'),
platform: 'win32',
env: { LOCALAPPDATA: join('/local') },
env: { LOCALAPPDATA: localAppData },
directoryExists: () => true
})
expect(resolved).toBe(join('/local', 'hermes', 'skills'))
expect(resolved).toBe(join(localAppData, 'hermes', 'skills'))
})
it('falls back to the dotfolder when Windows exposes no usable LOCALAPPDATA', () => {
@@ -43,7 +43,7 @@ test('@golden opens an unstaged file diff from Source Control', async ({
).toBeVisible()
await expect(orcaPage.locator('.editor-header-path').first()).toHaveAttribute(
'title',
`${realpathSync(path.join(fixture.worktreePath, GOLDEN_CHANGED_PATH))} (diff)`
`${realpathSync(path.join(fixture.worktreePath, GOLDEN_CHANGED_PATH)).replaceAll('\\', '/')} (diff)`
)
const probe = orcaPage.getByRole('button', { name: /Source Control/ })
+60 -22
View File
@@ -18,6 +18,11 @@ type LinkClientPoint = { x: number; y: number }
const LINK_SCAN_CHAR_LIMIT = 12_000
function canonicalFileIdentity(value: string): string {
const normalized = path.resolve(value).replaceAll('\\', '/')
return process.platform === 'win32' ? normalized.toLowerCase() : normalized
}
async function locateLink(page: Page, needle: string): Promise<LinkProbe | null> {
return page.evaluate((needle) => {
const state = window.__store?.getState()
@@ -34,11 +39,19 @@ async function locateLink(page: Page, needle: string): Promise<LinkProbe | null>
return null
}
const buffer = pane.terminal.buffer.active
for (let row = 0; row < pane.terminal.rows; row += 1) {
const line = buffer.getLine(buffer.viewportY + row)
const col = line?.translateToString(true).indexOf(needle) ?? -1
if (col >= 0) {
return { col: col + Math.floor(needle.length / 2), row, tabId }
// Preserve fixed-width cells so paths split across xterm rows stay searchable.
const visibleCells = Array.from({ length: pane.terminal.rows }, (_, row) =>
(buffer.getLine(buffer.viewportY + row)?.translateToString(false) ?? '').padEnd(
pane.terminal.cols
)
).join('')
const start = visibleCells.indexOf(needle)
if (start !== -1) {
const center = start + Math.floor(needle.length / 2)
return {
col: center % pane.terminal.cols,
row: Math.floor(center / pane.terminal.cols),
tabId
}
}
return null
@@ -162,14 +175,13 @@ test('opens a terminal file link and observes an external edit @golden', async (
await expect(explorerRow).toHaveAttribute('data-selected', 'true', { timeout: 10_000 })
await expect
.poll(
() =>
orcaPage.evaluate(
(expectedPath) => window.__monacoEditorE2E?.filePath === expectedPath,
filePath
async () =>
canonicalFileIdentity(
(await orcaPage.evaluate(() => window.__monacoEditorE2E?.filePath)) ?? ''
),
{ timeout: 20_000, message: 'Monaco opened a different file identity' }
)
.toBe(true)
.toBe(canonicalFileIdentity(filePath))
writeFileSync(filePath, `${original.trimEnd()}\n\n${changedMarker}\n`)
await expect
@@ -196,14 +208,18 @@ test('reuses a terminal file link already open in a sibling workspace @golden',
test.setTimeout(180_000)
await waitForSessionReady(orcaPage)
const sourceWorktreeId = await waitForActiveWorktree(orcaPage)
const sibling = await orcaPage.evaluate((sourceId) => {
const worktrees = await orcaPage.evaluate((sourceId) => {
const state = window.__store?.getState()
return (
Object.values(state?.worktreesByRepo ?? {})
.flat()
.find((worktree) => worktree.id !== sourceId) ?? null
)
const entries = Object.values(state?.worktreesByRepo ?? {}).flat()
return {
source: entries.find((worktree) => worktree.id === sourceId) ?? null,
sibling: entries.find((worktree) => worktree.id !== sourceId) ?? null
}
}, sourceWorktreeId)
const { source, sibling } = worktrees
if (!source) {
throw new Error('source worktree fixture unavailable')
}
if (!sibling) {
throw new Error('sibling worktree fixture unavailable')
}
@@ -230,7 +246,24 @@ test('reuses a terminal file link already open in a sibling workspace @golden',
await ensureTerminalVisible(orcaPage)
await waitForActiveTerminalManager(orcaPage, 30_000)
const ptyId = await waitForActivePanePtyId(orcaPage)
await orcaPage.evaluate(() => {
const state = window.__store?.getState()
state?.setSidebarOpen(false)
state?.setRightSidebarOpen(false)
})
await expect
.poll(
() =>
orcaPage.evaluate(() => {
const state = window.__store?.getState()
const tabId = state?.activeTabId
const manager = tabId ? window.__paneManagers?.get(tabId) : null
return manager?.getActivePane?.()?.terminal.cols ?? 0
}),
{ message: 'terminal did not expand after closing the sidebars' }
)
.toBeGreaterThan(120)
const ptyId = await waitForActivePanePtyId(orcaPage, 30_000)
await waitForPtyShellEcho(orcaPage, ptyId, 15_000)
const printedPath = process.platform === 'win32' ? filePath.replaceAll('\\', '/') : filePath
const command = nodeTerminalCommand(['-e', `console.log(${JSON.stringify(printedPath)})`])
@@ -261,13 +294,18 @@ test('reuses a terminal file link already open in a sibling workspace @golden',
await expect(editorHeader).toContainText('package.json', { timeout: 20_000 })
await expect
.poll(
() =>
orcaPage.evaluate(() => ({
filePath: window.__monacoEditorE2E?.filePath ?? null,
async () => {
const rendered = await orcaPage.evaluate(() => ({
filePath: window.__monacoEditorE2E?.filePath ?? '',
activeWorktreeId: window.__store?.getState()?.activeWorktreeId ?? null
})),
}))
return {
filePath: canonicalFileIdentity(rendered.filePath),
activeWorktreeId: rendered.activeWorktreeId
}
},
{ timeout: 20_000, message: 'sibling workspace never rendered the linked file' }
)
.toEqual({ filePath, activeWorktreeId: sibling.id })
.toEqual({ filePath: canonicalFileIdentity(filePath), activeWorktreeId: sibling.id })
await expect(orcaPage.getByText('Loading...', { exact: true })).toHaveCount(0)
})