Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/openclaw/qualify-windows-arm64.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
param(
[Parameter(Mandatory)][string]$ArtifactDirectory,
[Parameter(Mandatory)][string]$OutputDirectory,
[Parameter(Mandatory)][string]$Source
)
$ErrorActionPreference = 'Stop'
if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -ne 'Arm64') {
throw 'Qualification requires native Windows ARM64.'
}
$label = 'bun-webkit-windows-arm64'
$archive = Join-Path $ArtifactDirectory "$label.tar.gz"
$manifest = Get-Content -Raw (Join-Path $ArtifactDirectory "$label.manifest.json") | ConvertFrom-Json
$digest = (Get-FileHash -Algorithm SHA256 -LiteralPath $archive).Hash.ToLowerInvariant()
if ($manifest.source -cne $Source -or $manifest.lane.label -cne $label -or
$manifest.files."$label.tar.gz".sha256 -cne $digest -or
$manifest.files."$label.tar.gz".size -ne (Get-Item -LiteralPath $archive).Length) {
throw 'Windows ARM64 artifact provenance mismatch.'
}
New-Item -ItemType Directory -Path $OutputDirectory | Out-Null
& tar -xzf $archive -C $OutputDirectory
if ($LASTEXITCODE -ne 0) { throw 'Archive extraction failed.' }
$jsc = Join-Path $OutputDirectory 'bun-webkit/bin/jsc.exe'
$ffi = Join-Path $OutputDirectory 'bun-webkit/bin/testFFI.exe'
foreach ($exe in @($jsc, $ffi)) {
$bytes = [IO.File]::ReadAllBytes($exe)
$pe = [BitConverter]::ToInt32($bytes, 0x3c)
if ([BitConverter]::ToUInt16($bytes, $pe + 4) -ne 0xaa64) { throw "$exe is not an ARM64 PE." }
}
$probe = @'
function f(n) { let s = 0; for (let i = 0; i < n; ++i) s = (s + i * 7) % 1000003; return s; }
for (let i = 0; i < 1000; ++i) { if (f(100000) !== 545003) throw new Error("JIT result"); }
if (!(numberOfDFGCompiles(f) > 0 && numberOfDFGCompiles(f) < 1000000)) throw new Error("DFG unavailable");
print("windows-arm64-jsc-ok");
'@
$result = & $jsc -e $probe
if ($LASTEXITCODE -ne 0 -or $result -cne 'windows-arm64-jsc-ok') { throw 'Native JSC startup/JIT probe failed.' }
& $ffi
if ($LASTEXITCODE -ne 0) { throw 'Native testFFI failed.' }
@{ passed = $true; source = $Source; lane = $label; sha256 = $digest; architecture = 'arm64'; startup = $true; dfg = $true; ffi = $true } |
ConvertTo-Json | Set-Content -Encoding utf8 (Join-Path $OutputDirectory 'gate.json')
8 changes: 5 additions & 3 deletions .github/openclaw/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
This release adds engine PR #8's repeated stack-coordinate cache to the qualified OpenClaw engine on upstream `5718a6ec579b98362ea7276a426deedcc6281ef5`.
This release adds the non-LTO Windows ARM64 artifact to the qualified OpenClaw engine on upstream `5718a6ec579b98362ea7276a426deedcc6281ef5`, with native Windows 11 ARM64 startup, DFG JIT and FFI gates bound to the archive and source.

- Cache requested provider-relative stack line and column conversions under the existing line-table lock, preserving document offsets, builtin overrides, and persistent bytecode format; thanks @steipete. Regressions cover repeated reads, independent providers, UTF-16 columns, and compressed line-table boundaries across interpreter/JIT tiers and cache modes.
- Build Windows ARM64 with the unchanged upstream cross-compilation recipe and require both JSC and testFFI to be native ARM64 PE files.

The previously released engine changes remain included:

- Cache requested provider-relative stack line and column conversions under the existing line-table lock, preserving document offsets, builtin overrides, and persistent bytecode format; thanks @steipete. Regressions cover repeated reads, independent providers, UTF-16 columns, and compressed line-table boundaries across interpreter/JIT tiers and cache modes.

- Release free allocator memory from idle compiler threads and long Atomics waits; thanks @Jarred-Sumner.
- Preserve Segmenter surrogate boundaries, opt-in Proxy global prototypes, module-loader AsyncLocalStorage context, and per-VM worker heap/stack budgets; thanks @steipete and @robobun.
- Give require(esm) a separate live namespace with its own `__esModule` marker while preserving unmarked import namespaces; thanks @robobun.
Expand All @@ -12,7 +14,7 @@ The previously released engine changes remain included:
- Synchronize the accounting-related parallel-helper assertion and visited-memory counters.
- Preserve syntax-selected stack positions for calls, constructors, property reads and async continuations, including live/captured stacks and cache replay; thanks @robobun for the upstream source-position work.

Qualification includes all nine artifact builds, Linux JSC module/promise/namespace/Segmenter and allocation-sampling regressions, the W113 paired Bun selection, a build of the exact prepared upstream-synced Bun runtime with its required namespace API adapter and candidate manifest with startup and unchanged upstream memory-release tests, and a separate build with feature adapters for the complete fork selection and patch regressions. Syntax-selected stack positions are checked across execution tiers, live/captured stacks, and cache replay. Other targets have build/provenance proof; native runtime qualification is Linux x64.
Qualification includes all ten artifact builds, Linux JSC module/promise/namespace/Segmenter and allocation-sampling regressions, the W113 paired Bun selection, a build of the exact prepared upstream-synced Bun runtime with its required namespace API adapter and candidate manifest with startup and unchanged upstream memory-release tests, and a separate build with feature adapters for the complete fork selection and patch regressions. Syntax-selected stack positions are checked across execution tiers, live/captured stacks, and cache replay. Other targets have build/provenance proof; native runtime qualification covers Linux x64 and Windows ARM64 JSC startup, DFG JIT and FFI probes. Bun consumer qualification is separate.

The new engine requires Bun's updated mimalloc idle hook and matching embedding adapters. A manifest-only update of the original sync tree does not compile: its old namespace marker access must be replaced by the namespace adapter (Bun #106), paired atomically with this engine. Consumers must rebuild with the manifest's exact headers and libraries; an older executable is not interchangeable.

Expand Down
13 changes: 10 additions & 3 deletions .github/openclaw/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,16 @@ def assemble(inputs,out,source):
shutil.copyfile(f,out/name)
artifacts[name]={'url':f'https://github.com/{REPO}/releases/download/{tag}/{name}','sha256':sha(f),'size':f.stat().st_size}
lanes.append(manifest)
windows=json.loads((inputs/'windows-arm64-qualification/gate.json').read_text())
if (windows.get('passed') is not True or windows.get('source')!=source
or windows.get('lane')!='bun-webkit-windows-arm64' or windows.get('architecture')!='arm64'
or windows.get('sha256')!=artifacts['bun-webkit-windows-arm64.tar.gz']['sha256']
or any(windows.get(k) is not True for k in ('startup','dfg','ffi'))):
raise ValueError('missing or mismatched native Windows ARM64 qualification')
gate['windows_arm64']=windows
# Retain low-level provenance and test logs as a separate archive.
execute('tar','-czf',str(out/'provenance.tar.gz'),'-C',str(inputs),*[
item for lane in config['lanes'] for item in [lane['label']+'/provenance',lane['label']+'/'+lane['label']+'.manifest.json']], 'qualification')
item for lane in config['lanes'] for item in [lane['label']+'/provenance',lane['label']+'/'+lane['label']+'.manifest.json']], 'qualification', 'windows-arm64-qualification')
license_text='OpenClaw WebKit source and licenses\n\n'+f'Source: https://github.com/{REPO}/tree/{source}\nSource archive: https://github.com/{REPO}/archive/{source}.tar.gz\n\n'
for p in ['Source/JavaScriptCore/COPYING.LIB','Source/WTF/wtf/COPYING.LIB']:
file=ROOT/p
Expand Down Expand Up @@ -92,7 +99,7 @@ def authorize(source,run_id):
jobs=api('-H','Cache-Control: max-age=0',f'repos/{REPO}/actions/runs/{run_id}/jobs?filter=latest&per_page=100')
required=[j for j in jobs['jobs'] if j['name']!='publish']
expected_builds={'build ('+l['label']+')' for l in json.loads((ROOT/'.github/openclaw/variants.json').read_text())['lanes']}
if ({j['name'] for j in required}!=expected_builds|{'preflight','test','assemble'}
if ({j['name'] for j in required}!=expected_builds|{'preflight','test','windows-arm64','assemble'}
or any(j['conclusion']!='success' or j['run_attempt']!=run['run_attempt'] for j in required)):
raise ValueError('all exact build, test and assembly jobs must pass')
pending=api('-H','Cache-Control: max-age=0',f'repos/{REPO}/actions/runs/{run_id}/pending_deployments')
Expand Down Expand Up @@ -121,7 +128,7 @@ def publish(directory,source):
raise ValueError('publishing requires reviewed source on an explicitly permitted owned branch')
# Tag creation fails atomically if the name exists. Neither existing drafts nor releases are resumed.
api('-X','POST',f'repos/{REPO}/git/refs','-f','ref=refs/tags/'+tag,'-f','sha='+source)
body=f'OpenClaw-built WebKit at `{source}`. All nine lanes and Linux qualification passed.\n\n'+(ROOT/'.github/openclaw/release-notes.md').read_text()
body=f'OpenClaw-built WebKit at `{source}`. All {len(manifest["artifacts"])} lanes, Linux qualification and native Windows ARM64 engine probes passed.\n\n'+(ROOT/'.github/openclaw/release-notes.md').read_text()
draft=api('-X','POST',f'repos/{REPO}/releases','-f','tag_name='+tag,'-f','name='+tag,'-f','body='+body,'-F','draft=true')
release_id=draft['id']
execute('gh','release','upload',tag,'-R',REPO,*[str(p) for p in sorted(directory.iterdir())])
Expand Down
22 changes: 18 additions & 4 deletions .github/openclaw/test_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,23 @@ def setUp(self):
label=lane['label'];d=self.inputs/label;d.mkdir();(d/'provenance').mkdir()
f=d/(label+'.tar.gz');f.write_bytes(label.encode())
(d/(label+'.manifest.json')).write_text(json.dumps({'source':SOURCE,'lane':{'label':label},'files':{f.name:{'sha256':release.sha(f),'size':f.stat().st_size}}}))
w=self.inputs/'windows-arm64-qualification';w.mkdir()
archive=self.inputs/'bun-webkit-windows-arm64/bun-webkit-windows-arm64.tar.gz'
(w/'gate.json').write_text(json.dumps({'passed':True,'source':SOURCE,'lane':'bun-webkit-windows-arm64','architecture':'arm64','sha256':release.sha(archive),'startup':True,'dfg':True,'ffi':True}))
def test_windows_gate_rejects_wrong_source_digest_or_failed_probe(self):
p=self.inputs/'windows-arm64-qualification/gate.json';original=json.loads(p.read_text())
for key,value in [('source','b'*40),('sha256','0'*64),('architecture','x64'),('startup',False),('dfg',False),('ffi',False)]:
with self.subTest(key=key):
p.write_text(json.dumps({**original,key:value}))
with self.assertRaisesRegex(ValueError,'native Windows ARM64'):self.assemble()
import shutil
shutil.rmtree(self.out)
def test_windows_gate_is_required(self):
(self.inputs/'windows-arm64-qualification/gate.json').unlink()
with self.assertRaises(FileNotFoundError):self.assemble()
def assemble(self):release.assemble(self.inputs,self.out,SOURCE)
def test_complete_set_roundtrip(self):
self.assemble();self.assertEqual(len(release.verify(self.out,SOURCE)['artifacts']),9)
self.assemble();self.assertEqual(len(release.verify(self.out,SOURCE)['artifacts']),10)
def test_changed_lane_bytes_rejected(self):
next(self.inputs.glob('*/*.tar.gz')).write_bytes(b'tampered')
with self.assertRaises(ValueError):self.assemble()
Expand Down Expand Up @@ -77,7 +91,7 @@ def test_authorization_requires_every_exact_green_job(self):
def test_authorization_accepts_ref_qualified_path(self):
run={'head_sha':SOURCE,'head_branch':'openclaw/main','event':'workflow_dispatch','path':'.github/workflows/openclaw-artifacts.yml@openclaw/main','run_attempt':1}
config=json.loads((release.ROOT/'.github/openclaw/variants.json').read_text())
names=['preflight','test','assemble']+['build ('+l['label']+')' for l in config['lanes']]
names=['preflight','test','windows-arm64','assemble']+['build ('+l['label']+')' for l in config['lanes']]
jobs={'jobs':[{'name':n,'conclusion':'success','run_attempt':1} for n in names]}
pending=[{'environment':{'name':'openclaw-artifacts','id':99},'current_user_can_approve':True}]
with patch.object(release,'api',side_effect=[{'enabled':True},run,jobs,pending,{}]) as api,patch.object(release,'execute') as execute:
Expand All @@ -88,7 +102,7 @@ def test_authorization_accepts_ref_qualified_path(self):
def test_authorization_accepts_protected_batch_branch(self):
run={'head_sha':SOURCE,'head_branch':'openclaw/batch-1','event':'workflow_dispatch','path':'.github/workflows/openclaw-artifacts.yml@openclaw/batch-1','run_attempt':1}
config=json.loads((release.ROOT/'.github/openclaw/variants.json').read_text())
names=['preflight','test','assemble']+['build ('+l['label']+')' for l in config['lanes']]
names=['preflight','test','windows-arm64','assemble']+['build ('+l['label']+')' for l in config['lanes']]
jobs={'jobs':[{'name':n,'conclusion':'success','run_attempt':1} for n in names]}
pending=[{'environment':{'name':'openclaw-artifacts','id':99},'current_user_can_approve':True}]
with patch.object(release,'api',side_effect=[{'enabled':True},run,jobs,pending,{}]),patch.object(release,'execute') as execute:
Expand Down Expand Up @@ -133,7 +147,7 @@ def test_wrong_engine_gate_source_rejected(self):
def test_authorization_accepts_rebased_release_branch(self):
run={'head_sha':SOURCE,'head_branch':'openclaw/release-5718a6ec','event':'workflow_dispatch','path':'.github/workflows/openclaw-artifacts.yml','run_attempt':1}
config=json.loads((release.ROOT/'.github/openclaw/variants.json').read_text())
names=['preflight','test','assemble']+['build ('+l['label']+')' for l in config['lanes']]
names=['preflight','test','windows-arm64','assemble']+['build ('+l['label']+')' for l in config['lanes']]
jobs={'jobs':[{'name':n,'conclusion':'success','run_attempt':1} for n in names]}
pending=[{'environment':{'name':'openclaw-artifacts','id':99},'current_user_can_approve':True}]
with patch.object(release,'api',side_effect=[{'enabled':True},run,jobs,pending,{}]),patch.object(release,'execute') as execute:
Expand Down
6 changes: 5 additions & 1 deletion .github/openclaw/variants.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@
{
"label": "bun-webkit-windows-amd64-lto",
"consumer": "openclaw-release.yml optional windows-x64"
},
{
"label": "bun-webkit-windows-arm64",
"consumer": "openclaw-release.yml windows-arm64: release without LTO"
}
],
"excluded": "Windows arm64, Android, FreeBSD, debug and ASAN are not selected by the fork's active OpenClaw workflows. There is no separate x64 baseline WebKit archive.",
"excluded": "Android, FreeBSD, debug and ASAN are not selected by the fork's active OpenClaw workflows. Windows arm64 uses upstream's non-LTO lane because LLVM CodeView cannot encode its LTO register tuples. There is no separate x64 baseline WebKit archive.",
"upstream_sync": {
"base_commit": "d2d2a26ef973cdd97b37953f5dba58052acad74f",
"upstream_commit": "1878660bb47a6a87ebe518cea0581ef4bf9b71f8",
Expand Down
26 changes: 24 additions & 2 deletions .github/workflows/openclaw-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,30 @@ jobs:
${{ runner.temp }}/qualification/sync/runtime-cache/*.json
if-no-files-found: error
retention-days: 7
windows-arm64:
needs: build
runs-on: windows-11-arm
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
sparse-checkout: .github
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: bun-webkit-windows-arm64
path: ${{ runner.temp }}/windows-artifact
- name: Qualify native ARM64 engine
shell: pwsh
run: ./.github/openclaw/qualify-windows-arm64.ps1 -ArtifactDirectory "$env:RUNNER_TEMP/windows-artifact" -OutputDirectory "$env:RUNNER_TEMP/windows-proof" -Source $env:GITHUB_SHA
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: windows-arm64-qualification
path: ${{ runner.temp }}/windows-proof/gate.json
if-no-files-found: error
retention-days: 7
assemble:
needs: [build, test]
needs: [build, test, windows-arm64]
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
Expand Down Expand Up @@ -185,7 +207,7 @@ jobs:
retention-days: 7
publish:
if: inputs.publish == true && (github.ref == 'refs/heads/openclaw/main' || github.ref == 'refs/heads/openclaw/batch-1' || github.ref == 'refs/heads/openclaw/release-5718a6ec')
needs: [build, test, assemble]
needs: [build, test, windows-arm64, assemble]
environment: openclaw-artifacts
permissions:
contents: write
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/openclaw-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,60 @@ concurrency:
group: openclaw-engine-checks-${{ github.ref }}
cancel-in-progress: true
jobs:
windows-arm64-build:
if: github.repository == 'openclaw/WebKit'
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 150
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 64
persist-credentials: false
sparse-checkout-cone-mode: false
sparse-checkout: |
/*
!/LayoutTests/
!/JSTests/
!/PerformanceTests/
!/Websites/
!/ManualTests/
!/WebDriverTests/
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: '26.3.0'
- name: Build unchanged upstream Windows ARM64 lane
env:
BUILDX_METADATA_PROVENANCE: max
run: python3 .github/openclaw/artifacts.py build --lane bun-webkit-windows-arm64 --output "$RUNNER_TEMP/windows-artifact"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: bun-webkit-windows-arm64
path: ${{ runner.temp }}/windows-artifact
compression-level: 0
if-no-files-found: error
retention-days: 7
windows-arm64:
needs: windows-arm64-build
runs-on: windows-11-arm
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
sparse-checkout: .github
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: bun-webkit-windows-arm64
path: ${{ runner.temp }}/windows-artifact
- name: Qualify native ARM64 engine
shell: pwsh
run: ./.github/openclaw/qualify-windows-arm64.ps1 -ArtifactDirectory "$env:RUNNER_TEMP/windows-artifact" -OutputDirectory "$env:RUNNER_TEMP/windows-proof" -Source $env:GITHUB_SHA
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: windows-arm64-qualification
path: ${{ runner.temp }}/windows-proof/gate.json
if-no-files-found: error
retention-days: 7
linux-native:
if: github.repository == 'openclaw/WebKit'
runs-on: blacksmith-32vcpu-ubuntu-2404
Expand Down
Loading