Skip to content

Commit 39b5327

Browse files
committed
기능: 하드웨어 WebGPU 결과 오라클을 제품화
기존 pyproc/gpu에 닫힌 WebGPU provider와 version 1 결과 영수증을 추가했다. compute와 RGBA8 결과를 hostcall 경계에서 검증하고 오래된 수동 probe를 제거했다. 설치 제품과 headed Edge에서 AMD RDNA 3 nonfallback 결과 및 자원 정리를 확인했다. 음성 시험: expected pixel 변이는 PYPROC_GPU_RESULT_MISMATCH와 오차 3으로 RED였다. 검증: npm test, contracts, types, package, installed, browser, Control, MCP, hardware gate GREEN.
1 parent 595b504 commit 39b5327

44 files changed

Lines changed: 1059 additions & 799 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/hardware-gpu.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# 실제 hardware WebGPU 결과 gate. 표준 hosted runner에는 GPU가 없으므로 interactive Windows
2+
# self-hosted runner에 pyproc-webgpu label을 명시적으로 붙인 경우에만 workflow_dispatch로 실행한다.
3+
name: hardware-gpu
4+
5+
on:
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
11+
concurrency:
12+
group: hardware-gpu-${{ github.ref }}
13+
cancel-in-progress: false
14+
15+
jobs:
16+
visual-oracle:
17+
runs-on: [self-hosted, Windows, X64, pyproc-webgpu]
18+
timeout-minutes: 20
19+
steps:
20+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
21+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
22+
with:
23+
node-version: 22
24+
- run: npm ci
25+
- name: Hardware Edge 확인
26+
shell: pwsh
27+
run: |
28+
$edge = 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
29+
if (-not (Test-Path -LiteralPath $edge -PathType Leaf)) {
30+
throw "Microsoft Edge executable not found: $edge"
31+
}
32+
Write-Host "Microsoft Edge $((Get-Item -LiteralPath $edge).VersionInfo.ProductVersion)"
33+
- run: npm run test:hardware-visual-oracle
34+
env:
35+
PYPROC_BROWSER: 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
36+
PYPROC_HARDWARE_EVIDENCE_DIR: hardwareGpuEvidence
37+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
38+
if: always()
39+
with:
40+
name: hardware-gpu-evidence
41+
path: hardwareGpuEvidence
42+
if-no-files-found: error

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Thumbs.db
1414
.vscode/
1515
.idea/
1616
scratch/
17+
hardwareGpuEvidence/
1718
*.local
1819
!scripts/engineBuilder/Setup.local
1920
!scripts/engineBuilder/Setup.data.local

README.ko.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,21 @@ tag, `Requires-Python`, marker, yanked policy를 검사한다. 선별된 native
8787
relay, ASGI, process, GPU, clipboard, framebuffer, artifact effect는 명시적 authority를 요구하고 version이
8888
있는 hostcall ABI를 지난다. Browser object와 live Python object는 durable state에 저장되지 않는다.
8989

90+
hardware WebGPU도 같은 request-scoped 경계 뒤에 둔다. `pyproc/gpu`는 등록된 operation만 제공하고 guest에
91+
device를 노출하는 대신 versioned 결과 영수증을 반환한다.
92+
93+
```js
94+
import { createWebGpuHostAdapter, runHardwareVisualOracle } from "pyproc/gpu";
95+
96+
const gpu = await createWebGpuHostAdapter({ requireHardware: true });
97+
try {
98+
const receipt = await runHardwareVisualOracle(gpu);
99+
console.log(receipt.state, receipt.adapter.class);
100+
} finally {
101+
gpu.close();
102+
}
103+
```
104+
90105
## WebComputer
91106

92107
`createWebComputer()`는 소유 kernel guest와 WebMachine device, signed image, ownership, 선택적 durable
@@ -102,7 +117,7 @@ Machine image 계약을 사용한다.
102117
| `pyproc/machine` | WebMachine host, device, image, fleet, kernel guest |
103118
| `pyproc/assets` | same-origin worker asset manifest와 integrity 검증 |
104119
| `pyproc/wasi` | 저수준 session, kernel, hostcall, package, factory 계약 |
105-
| `pyproc/gpu` | GPU host adapter |
120+
| `pyproc/gpu` | 닫힌 WebGPU host adapter와 versioned hardware 결과 oracle |
106121
| `pyproc/socket` | Socket relay host adapter |
107122
| `pyproc/control` | Local control protocol client와 registry |
108123

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ const port = new ProductHostCapabilityPort({
107107
port.install(broker);
108108
```
109109

110+
Hardware WebGPU stays behind the same request-scoped boundary. `pyproc/gpu` exposes only registered
111+
operations and returns a versioned result receipt instead of exposing a device to the guest.
112+
113+
```js
114+
import { createWebGpuHostAdapter, runHardwareVisualOracle } from "pyproc/gpu";
115+
116+
const gpu = await createWebGpuHostAdapter({ requireHardware: true });
117+
try {
118+
const receipt = await runHardwareVisualOracle(gpu);
119+
console.log(receipt.state, receipt.adapter.class);
120+
} finally {
121+
gpu.close();
122+
}
123+
```
124+
110125
## WebComputer
111126

112127
`createWebComputer()` composes the owned kernel guest with WebMachine devices, signed images, ownership,
@@ -122,7 +137,7 @@ contract as root `boot()` and `open()`.
122137
| `pyproc/machine` | WebMachine host, devices, images, fleet, and kernel guest |
123138
| `pyproc/assets` | Same-origin worker asset manifest and integrity verification |
124139
| `pyproc/wasi` | Low-level session, kernel, hostcall, package, and factory contracts |
125-
| `pyproc/gpu` | GPU host adapter |
140+
| `pyproc/gpu` | Closed WebGPU host adapter and versioned hardware result oracle |
126141
| `pyproc/socket` | Socket relay host adapter |
127142
| `pyproc/control` | Local control protocol client and registries |
128143

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type PyProcErrorCode =
3939
| "PYPROC_PARTICIPANT_LEFT"
4040
| "PYPROC_KERNEL_EXECUTION_ERROR"
4141
| "PYPROC_GPU_UNAVAILABLE"
42+
| "PYPROC_GPU_RESULT_MISMATCH"
4243
| "PYPROC_INTERNAL";
4344

4445
export const PYPROC_ERROR_CODES: readonly PyProcErrorCode[];

mainPlan/9-agentComputerStandardReadiness/README.md

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Initiative 9: agent computer와 웹 표준 후보 준비도
22

3-
상태: **진행 중, M0과 M1 완료, M2 진행 중**
3+
상태: **진행 중, M0부터 M2 완료, M3 진행 중**
44

55
이 문서는 agent가 pyproc에 들어와 보고, 행동하고, 계산하고, 세션을 넘겨 계속 일하는 전 과정을
66
제품 기준으로 끌어올리는 실행 원장이다. 기존 능력 점수와 증거의 정본은
@@ -14,10 +14,10 @@
1414

1515
| 질문 | 현재 판정 | 실사용 증거 | 완성까지 남은 것 |
1616
|---|---|---|---|
17-
| agent 진입점 | 매우 강함, 장기 수명주기 검증은 계속 | exact install 뒤 package engine 자동 선택, effect-free doctor, 네 adapter의 같은 CPython 첫 결과가 공개 계약으로 완결 | hardware GPU 결과 oracle과 이후 몸체 확대 |
18-
| 눈과 팔 | 매우 강함, 완성 아님, 북극성 9.3 | APX Situation, 20회 무잔류 수명주기, stale, ambiguous, occluded, navigation의 2후보, 1재관찰, effect retry 0회 수렴 영수증 | 실제 GPU visual oracle 확대 |
19-
| 비-agent 컴퓨팅 몸체 | 훌륭한 브라우저 컴퓨터, 로컬 OS 완전 대체는 아님 | owned CPython, worker process, OPFS disk, checkpoint, Machine image, Python과 x86 guest gate | 임의 native wheel, shared-memory thread, wasm 도구층, Node guest, quota 축출 계약 |
20-
| 단독 자립성 | Python 기본 Machine은 높음, 전체 WebComputer는 미완성 | source-built CPython과 stdlib가 npm에 포함되고 기본 부팅의 제3자 요청은 0 | x86 emulator와 firmware의 독립 재현, GPU 실기 CI, 브라우저 범위 확대 |
17+
| agent 진입점 | 매우 강함, 장기 수명주기 검증은 계속 | exact install 뒤 package engine 자동 선택, effect-free doctor, 네 adapter의 같은 CPython 첫 결과가 공개 계약으로 완결 | M3 컴퓨팅 몸체 확대와 독립 구현 conformance |
18+
| 눈과 팔 | 매우 강함, 완성 아님, 북극성 9.5 | APX Situation, 20회 무잔류 수명주기, bounded action 수렴, 실제 hardware compute와 pixel 결과 영수증 | 두 번째 독립 hardware와 browser 구현의 visual conformance |
19+
| 비-agent 컴퓨팅 몸체 | 훌륭한 브라우저 컴퓨터, 로컬 OS 완전 대체는 아님, 북극성 7.8 | owned CPython, worker process, OPFS disk, checkpoint, Machine image, Python과 x86 guest gate, hardware GPU 결과 gate | 임의 native wheel, 넓은 package reach, shared-memory thread, wasm 도구층, Node guest, quota 축출 계약 |
20+
| 단독 자립성 | Python 기본 Machine은 높음, 전체 WebComputer는 미완성 | source-built CPython과 stdlib가 npm에 포함되고 기본 부팅의 제3자 요청은 0 | x86 emulator와 firmware의 독립 재현, 외부 hardware runner 등록, 브라우저 범위 확대 |
2121
| 웹 표준 후보 가능성 | 기반은 있음, 후보라고 부르기에는 이름 | WebAssembly, Worker, cross-origin isolation, bucket file system 같은 표준 기반 위에 제품 계약이 동작 | vendor-neutral specification, 독립 구현, WPT형 conformance, 공개 incubation과 wide review |
2222

2323
첫 Pages 진입에서는 COI Service Worker가 문서를 실제 교체했다. 기존 affordance 실행은
@@ -95,7 +95,7 @@ implementation experience는 독립적이고 상호운용 가능한 구현, 저
9595

9696
### M2. Eyes와 Arms 장기 수명주기
9797

98-
판정: **진행 중**
98+
판정: **완료**
9999

100100
- 완료: legacy 의미 관찰을 같은 document epoch에 고정된 1,000개 이하 page로 순회한다. single-use
101101
continuation, 5분 TTL, 10,000 node와 16 MiB 전체 상한, page와 prefix 및 전체 digest, screenshot과 event
@@ -106,11 +106,14 @@ implementation experience는 독립적이고 상호운용 가능한 구현, 저
106106
packed Control 제품 20회와 NativeCdpSpace, FrameSpace, MCP, Python adapter가 0 수렴을 검증한다.
107107
- 완료: stale, ambiguous, occluded, navigation 교체를 후보 최대 2개, 재관찰 최대 1회, effect retry 0회,
108108
첫 effect 전 30000 ms로 고정한다. 성공과 안전 거절은 같은 version 1 영수증을 반환한다.
109-
- hardware GPU runner에서 pixel 결과 oracle을 추가해 수동 증거 상한을 제거한다.
109+
- 완료: installed `pyproc/gpu`의 닫힌 WebGPU provider를 hostcall 경계에 연결하고 실제 nonfallback
110+
hardware에서 compute와 RGBA8 pixel 결과를 version 1 oracle receipt로 검증한다. adapter, buffer,
111+
texture, target, process와 임시 profile을 모두 정리하며 software fallback과 결과 불일치는 RED다.
110112

111113
### M3. 컴퓨팅 몸체 확대
112114

113-
- `tests/northStar.mjs``localPythonParity`, `parallelProcesses`, `durableDisk` next를 순서대로 소진한다.
115+
- 진행 중: `tests/northStar.mjs``localPythonParity.packageReach`부터 시작하고 `parallelProcesses`,
116+
`durableDisk` next를 순서대로 소진한다.
114117
- upstream이 열어 주는 thread와 dynamic linking은 capability detection과 exact failure로 받는다.
115118
- wasm 도구층을 먼저 넣고 Node guest는 같은 Machine lifecycle과 image 계약을 통과시킨다.
116119

@@ -315,5 +318,31 @@ implementation experience는 독립적이고 상호운용 가능한 구현, 저
315318
지속 가림과 outcome unknown 1회 무재시도를 같은 영수증으로 증명한다.
316319
- action 수렴 gate 이빨: `ACTION_CONVERGENCE_MAX_ATTEMPTS`를 2에서 3으로 바꾼 음성 변형은
317320
`3 !== 2`를 보고 RED였고, 2로 복원한 같은 계약 gate는 GREEN이다.
318-
- 다음 직렬 작업: hardware GPU runner에서 pixel과 compute 결과 oracle을 실행해 정확한 command byte
319-
검증에 머문 수동 증거 상한을 제거한다.
321+
- 다음 직렬 작업: 아래 7절의 hardware 결과 oracle을 완료한 뒤 M3 `packageReach`로 이동한다.
322+
323+
## 7. 해결 판정: hardware visual과 compute 결과 oracle
324+
325+
- 첫 불일치: exact packed `pyproc@0.0.22``pyproc/gpu`는 기존 주입 adapter만 내보냈고
326+
`createWebGpuHostAdapter`가 없었다. 반면 headed Edge의 raw WebGPU는 AMD RDNA 3 nonfallback adapter에서
327+
vector `[5,2,5,1]`과 RGBA8 `[64,128,191,255]`를 오차 0으로 반환했다. 기존 수동 GPU probe 일곱 개는
328+
삭제된 `GpuCompute``enableGpu()`를 import했고 `tests/shaderDigests.json`은 어떤 gate도 읽지 않았다.
329+
- 제품 계약: `src/runtime/gpuOracle.js`가 등록된 WGSL, CPU expected value, digest와 typed mismatch를
330+
소유한다. `src/capabilities/webGpuHostAdapter.js``vectorAdd``solidRgba8`만 받고 device, pipeline,
331+
buffer, texture와 readback 수명을 소유한다. `pyproc/gpu`는 새 root나 subpath 없이 둘을 조립한다.
332+
- 설치 제품 증거: 빈 app에 설치한 tarball의 bare `pyproc/gpu``pyproc/wasi`만 import했다. 공개 Control이
333+
연 headed Edge에서 AMD RDNA 3, `isFallbackAdapter: false`, hostcall 2회를 확인했다. compute expected와
334+
actual digest는 모두 `sha256:f30cec7be08a06afc4c889f52d2d8bcfb66cc66328187ce9bf30ced10e670fa8`,
335+
pixel digest는 모두 `sha256:048402e9c4d980772355b55651974f77b1eca456a62116b54bfce1871d725ff1`,
336+
두 결과 오차는 0이었다.
337+
- 시각 검수: terminal 화면 PNG는 922 x 920이고 adapter, compute error 0, pixel error 0, hostcall 2와
338+
expected color swatch를 직접 확인했다. artifact SHA-256은
339+
`ba2933ed74bfb66e8304c4cf32deb77b331d638b0427cb8be752c94451d8043a`다. 제품 gate는 Control artifact를
340+
삭제하고 detach, target close, adapter close와 임시 profile 정리를 검증한다.
341+
- 지속 실행 경계: `.github/workflows/hardware-gpu.yml``pyproc-webgpu` label의 interactive Windows
342+
self-hosted runner에서 수동 dispatch하고 screenshot과 JSON을 artifact로 남긴다. 현재 증거는 로컬 MSI
343+
hardware 실행이며 외부 self-hosted runner의 등록이나 workflow 실행은 아직 증거로 주장하지 않는다.
344+
- gate 이빨: expected pixel 첫 channel을 64에서 67로 바꾼 음성 변형은
345+
`PYPROC_GPU_RESULT_MISMATCH`, `stage: pixel`, `maxChannelError: 3`으로 RED였다. 64로 복원한 같은
346+
hardware oracle 계약은 GREEN이다.
347+
- 다음 직렬 작업: M3 `localPythonParity.packageReach`다. GPU 축 자체의 다음 ceiling은 두 번째 독립
348+
hardware와 browser 구현에서 같은 receipt를 통과시키는 `multiVendorVisualConformance`로 남긴다.

mainPlan/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
## 현재 상태
1919

2020
현재 미완료 이니셔티브는 [9-agentComputerStandardReadiness](9-agentComputerStandardReadiness/README.md)다.
21-
M2의 wrong-effect 없는 action 수렴까지 완료했고 hardware GPU 결과 oracle부터 직렬로 이어간다.
21+
M2의 hardware GPU 결과 oracle까지 완료했고 M3의 `packageReach`부터 직렬로 이어간다.
2222

2323
이 번호는 agent-computer 포트폴리오의 실행 순서다. North Star ceiling ladder의 기존 번호와 우선순위를
2424
대체하지 않는다.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
"default": "./scripts/controlProtocol/controlApi.js"
4646
},
4747
"./gpu": {
48-
"types": "./src/capabilities/productHostCapabilities.d.ts",
49-
"default": "./src/capabilities/productHostCapabilities.js"
48+
"types": "./src/composition/gpuSubpath.d.ts",
49+
"default": "./src/composition/gpuSubpath.js"
5050
},
5151
"./socket": {
5252
"types": "./src/capabilities/productHostCapabilities.d.ts",
@@ -113,6 +113,7 @@
113113
"test:browser-control-stress": "node tests/browser/browserControlStress.mjs",
114114
"test:automation-lifecycle": "node tests/browser/automationLifecycleProduct.mjs",
115115
"test:action-convergence": "node tests/browser/actionConvergenceProduct.mjs",
116+
"test:hardware-visual-oracle": "node tests/browser/hardwareVisualOracleProduct.mjs",
116117
"test:browser": "node tests/browser/run.mjs",
117118
"test:examples": "node tests/browser/examples.mjs",
118119
"bench:speed": "node tests/browser/speedBench.mjs",

skills/catalog.json

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"format": "pyproc-skill-catalog",
33
"version": 1,
4-
"generatedFromDigest": "sha256:ffd94c4cf7683e2517b3f8c726e49e8985c51207ca2441abf5173743915b4e7d",
5-
"catalogDigest": "sha256:a9a610285b9000113f6c8c536c43c3b63ab35b37100f4d9b2e5a89f28940db61",
4+
"generatedFromDigest": "sha256:bfbeea984f8ce029b13c4b20c3a617e7c3d3692c3774b68a54aa43282927604b",
5+
"catalogDigest": "sha256:5d6ed1c272f74c59e87a455637ee0916d04437cdc5b37bfd3b53265f2c6a5fae",
66
"skills": [
77
{
88
"name": "automate-browser-with-pyproc",
@@ -299,8 +299,8 @@
299299
},
300300
{
301301
"path": "skills/develop-pyproc/references/module-boundaries.md",
302-
"bytes": 14465,
303-
"sha256": "sha256:6e1f8af7ffa98239b505a6c29a5ed97ba80f4b96f539b11f1a70fa69069774d8"
302+
"bytes": 14946,
303+
"sha256": "sha256:9e97acf95dbe33a2530569c1b8c93606070ef24aaba0dc1d14ec5cc776ff1224"
304304
},
305305
{
306306
"path": "skills/develop-pyproc/references/operating-model.md",
@@ -314,13 +314,13 @@
314314
},
315315
{
316316
"path": "skills/evolve-pyproc/references/contract-reality.md",
317-
"bytes": 1728,
318-
"sha256": "sha256:2fe5268e1a12005bf9de44c7d3683665c9069d05a681a82e6905bec1f6458a2f"
317+
"bytes": 2004,
318+
"sha256": "sha256:b8983fc6be818325a662c4ff786dfe95987ecb224c8ba12b498fcdb5e1e09814"
319319
},
320320
{
321321
"path": "skills/evolve-pyproc/references/experimental-freeze.md",
322-
"bytes": 1791,
323-
"sha256": "sha256:1e4fd4f5a9deb1586a0ec79285185ee2f3236a4d87b5ed618ea0267d926d80e6"
322+
"bytes": 2141,
323+
"sha256": "sha256:e34b0749073ce34b1b602435c2b04157c4e17dfe35279de9679d7e0e7d27f7fb"
324324
},
325325
{
326326
"path": "skills/evolve-pyproc/references/initiative-lessons.md",
@@ -339,8 +339,8 @@
339339
},
340340
{
341341
"path": "skills/reference-pyproc-api/references/api.md",
342-
"bytes": 4072,
343-
"sha256": "sha256:f8cb3812c80595fd28b9f73d84056218ea47970e7fff2da6f6338b49ec04835f"
342+
"bytes": 4548,
343+
"sha256": "sha256:4f0ee979806c6079e25f8329c334bd610123e1629cd1507203a4f436ebd33a7f"
344344
},
345345
{
346346
"path": "skills/reference-pyproc-api/references/bundle-format.md",
@@ -419,13 +419,13 @@
419419
},
420420
{
421421
"path": "skills/use-pyproc-runtime/references/consumer-contract.md",
422-
"bytes": 3155,
423-
"sha256": "sha256:03234c262c681f4614b41caf94d73fcc1bd48e63c17592a7ce42b6b757fae321"
422+
"bytes": 3202,
423+
"sha256": "sha256:31e4f74fd03fa6460f8609b15d2f03eaf90e43e6d7a72cb97ce5048cb3146211"
424424
},
425425
{
426426
"path": "skills/use-pyproc-runtime/references/hostcall-abi.md",
427-
"bytes": 8187,
428-
"sha256": "sha256:6c366bf08c5eccf667b479eaa3c81f727b1e6742e5a03fe66e712a298d4d642f"
427+
"bytes": 8538,
428+
"sha256": "sha256:bc6a8b7453c1e1f42988c091241242e11b47b4d534589830972c738e3726c8b4"
429429
},
430430
{
431431
"path": "skills/use-pyproc-runtime/references/kernel-contracts.md",
@@ -509,8 +509,8 @@
509509
},
510510
{
511511
"path": "skills/verify-pyproc/references/testing.md",
512-
"bytes": 1906,
513-
"sha256": "sha256:a38f955969f634ca7134d3b775eabdbe6ad1bbb2b768951cc244556867928761"
512+
"bytes": 2716,
513+
"sha256": "sha256:fbe4bdd0ef67d29b05bec290ae57f08769f85e22bf0510647b932abbec87cf95"
514514
}
515515
]
516516
}

skills/develop-pyproc/references/module-boundaries.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ The current clusters are:
3838
| `service` | Syscalls, ASGI, virtual origins, and terminals |
3939
| `environment` | Wheel cache, device filesystem, and init |
4040

41+
The existing Experimental GPU surface follows the same direction. `src/runtime/gpuOracle.js` owns the
42+
provider-neutral registered programs, expected values, digest receipt, and mismatch error. The optional
43+
`src/capabilities/webGpuHostAdapter.js` owns WebGPU device, pipeline, buffer, texture, and readback lifecycle.
44+
`src/composition/gpuSubpath.js` only gathers those contracts under `pyproc/gpu`; it does not move a browser
45+
device or arbitrary WGSL across the hostcall boundary.
46+
4147
## Policy and mechanism
4248

4349
Keep state-changing mechanisms separate from pure policy such as input normalization and budget checks.

0 commit comments

Comments
 (0)