Skip to content

Commit 40fe94a

Browse files
committed
기능: NumPy 데이터 프로필을 제품화
공식 NumPy 2.5.1 소스와 고정 도구체인을 data-3 엔진에 포함했다. multi-wheel catalog와 반복 설치, 복제, 이미지 복원 경계를 안전하게 닫았다. 문제: 기존 data profile에는 실제 과학 패키지와 static module 재설치 계약이 없었다. 원장에는 재현 build와 실사용 증거, 남은 제한을 기록했다. 검증: npm test; npm run test:types; npm run test:browser 검증: npm run test:installed; npm run test:package 검증: npm run test:control-product; npm run test:mcp-product 검증: npm run test:web-computer; npm run test:web-machine 검증: npm run test:hardware-visual-oracle; npm run assets:provenance 검증: npm run test:engine-independence; npm run skills:check
1 parent 6e7c5a8 commit 40fe94a

46 files changed

Lines changed: 2123 additions & 245 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/owned-engine.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
paths:
88
- ".gitattributes"
99
- "scripts/engineBuilder/**"
10+
- "scripts/scientificPackageBuilder/**"
1011
- "tests/browser/ownedEngineCoreProduct.html"
1112
- "tests/browser/ownedEngineDataProduct.html"
1213
- "tests/browser/ownedEngineProfileProduct.js"

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ happen only on an explicit maintainer decision; the Unreleased section accumulat
1717
- **A reproducible data engine now ships beside the default core engine.** `pyproc/wasi` exposes its exact
1818
manifest and a profile-selected package-owned catalog. The static `pyproc.data/2` module executes float64
1919
buffer addition and dot products with `wasm-simd128`, and its verified facade survives process clone and
20-
Machine image revival. Representative NumPy, SciPy, pandas, and Polars imports remain explicitly unsupported.
20+
Machine image revival. The exact data profile now also builds NumPy 2.5.1 from its official sdist as 13
21+
static modules, installs its verified Python layer from the package-owned multi-wheel catalog, and preserves
22+
it through process clone and Machine image revival. SciPy, pandas, Polars, and arbitrary native wheels remain
23+
explicitly unsupported.
2124

2225
## 0.0.22 - 2026-08-15
2326

README.ko.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,29 @@ import { createOwnedPackageResolver, getDataKernelEngineManifest } from "pyproc/
9898
const dataMachine = await boot({ engineManifest: await getDataKernelEngineManifest() });
9999
const dataResolver = await createOwnedPackageResolver({ profile: "data" });
100100
const dataPackages = dataMachine.createPackageEnvironment({ resolver: dataResolver });
101-
await dataPackages.install({ requirements: ["pyproc-native-data==1.0.0"] });
102-
await dataMachine.run("import pyproc_native_data; print(pyproc_native_data.inspect())");
101+
await dataPackages.install({ requirements: [
102+
"pyproc-native-data==1.0.0",
103+
"numpy==2.5.1",
104+
] });
105+
await dataMachine.run(`
106+
import numpy as np
107+
import pyproc_native_data
108+
print(pyproc_native_data.inspect())
109+
print(np.linalg.solve(np.array([[3., 1.], [1., 2.]]), np.array([9., 8.])))
110+
`);
103111
```
104112

105113
이 facade는 `pyproc.data/2`를 보고하며 float64 buffer 덧셈과 내적을 `wasm-simd128`로 실행한다.
106-
NumPy, SciPy, pandas, Polars 또는 임의 native wheel 호환성을 주장하지 않는다.
114+
같은 catalog는 NumPy 2.5.1 Python layer를 포함하고 정확한 data engine은 13개 native module을 built-in으로
115+
가진다. 두 wheel은 실행 중 network 요청 없이 package byte에서 설치된다. SciPy, pandas, Polars와 임의
116+
native wheel은 계속 지원 범위 밖이다.
107117

108-
catalog는 wrapper wheel, native source digest, ABI, engine ID, profile을 함께 봉인한다. 하나라도 다르면
118+
NumPy build는 source-pinned sdist와 정확한 Cython, Ninja, WASI SDK, CPython 입력을 쓴다. 현재 WASI C++
119+
runtime에는 exception 구현이 없으므로 memory 할당 실패나 pocketfft 내부 invariant 위반은 process를
120+
중단할 수 있다. 빈 FFT 같은 일반 입력 오류는 Python exception으로 유지된다.
121+
122+
catalog는 두 wheel, scientific source와 build receipt, native source digest, ABI, engine ID, profile을 함께
123+
봉인한다. 하나라도 다르면
109124
install 명령이 kernel에 도달하기 전에 실패한다. 검증된 package layer는 process clone과 Machine image에도
110125
따라가며, 새 worker가 보기 전에 image import가 포함 wheel의 digest를 다시 검사한다.
111126

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,29 @@ import { createOwnedPackageResolver, getDataKernelEngineManifest } from "pyproc/
102102
const dataMachine = await boot({ engineManifest: await getDataKernelEngineManifest() });
103103
const dataResolver = await createOwnedPackageResolver({ profile: "data" });
104104
const dataPackages = dataMachine.createPackageEnvironment({ resolver: dataResolver });
105-
await dataPackages.install({ requirements: ["pyproc-native-data==1.0.0"] });
106-
await dataMachine.run("import pyproc_native_data; print(pyproc_native_data.inspect())");
105+
await dataPackages.install({ requirements: [
106+
"pyproc-native-data==1.0.0",
107+
"numpy==2.5.1",
108+
] });
109+
await dataMachine.run(`
110+
import numpy as np
111+
import pyproc_native_data
112+
print(pyproc_native_data.inspect())
113+
print(np.linalg.solve(np.array([[3., 1.], [1., 2.]]), np.array([9., 8.])))
114+
`);
107115
```
108116

109117
This facade reports `pyproc.data/2` and runs float64 buffer addition and dot products through
110-
`wasm-simd128`. It does not claim NumPy, SciPy, pandas, Polars, or arbitrary native-wheel compatibility.
118+
`wasm-simd128`. The same catalog includes the NumPy 2.5.1 Python layer while the exact data engine embeds its
119+
13 native modules. Both wheels install from package bytes without a runtime network request. SciPy, pandas,
120+
Polars, and arbitrary native wheels remain unsupported.
111121

112-
The catalog seals the wrapper wheel, native source digest, ABI, engine ID, and profile. A mismatch fails before
122+
The NumPy build uses a source-pinned sdist and exact Cython, Ninja, WASI SDK, and CPython inputs. The current
123+
WASI C++ runtime has no exception implementation, so allocation failure or an internal pocketfft invariant may
124+
abort the process. Normal input errors such as an empty FFT remain Python exceptions.
125+
126+
The catalog seals both wheels, scientific source and build receipts, native source digests, ABI, engine ID, and
127+
profile. A mismatch fails before
113128
the install command reaches the kernel. Verified package layers also travel with process clones and Machine
114129
images. Image import rechecks every embedded wheel digest before a fresh worker sees it.
115130

mainPlan/9-agentComputerStandardReadiness/README.md

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
|---|---|---|---|
1717
| agent 진입점 | 매우 강함, 장기 수명주기 검증은 계속 | exact install 뒤 package engine 자동 선택, effect-free doctor, 네 adapter의 같은 CPython 첫 결과가 공개 계약으로 완결 | M3 컴퓨팅 몸체 확대와 독립 구현 conformance |
1818
| 눈과 팔 | 매우 강함, 완성 아님, 북극성 9.5 | APX Situation, 20회 무잔류 수명주기, bounded action 수렴, 실제 hardware compute와 pixel 결과 영수증 | 두 번째 독립 hardware와 browser 구현의 visual conformance |
19-
| 비-agent 컴퓨팅 몸체 | 훌륭한 브라우저 컴퓨터, 로컬 OS 완전 대체는 아님, 북극성 8.4 | owned CPython, worker process, OPFS disk, checkpoint, Machine image, Python과 x86 guest gate, hardware GPU 결과 gate, source-pinned core와 SIMD data package | 실제 scientific 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 등록, 브라우저 범위 확대 |
19+
| 비-agent 컴퓨팅 몸체 | 훌륭한 브라우저 컴퓨터, 로컬 OS 완전 대체는 아님, 북극성 8.7 | owned CPython, worker process, OPFS disk, checkpoint, Machine image, Python과 x86 guest gate, hardware GPU 결과 gate, source-pinned SIMD와 NumPy 2.5.1 data package | shared-memory thread, wasm 도구층, Node guest, 과학 패키지 폭, quota 축출 계약 |
20+
| 단독 자립성 | Python 기본과 data Machine은 높음, 전체 WebComputer는 미완성 | source-built CPython, stdlib, NumPy가 npm에 포함되고 기본 부팅과 package install의 제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 실행은
@@ -118,8 +118,10 @@ implementation experience는 독립적이고 상호운용 가능한 구현, 저
118118
compiled built-in facade의 설치와 import를 packed browser 제품 gate로 닫았다.
119119
- 완료: 별도 `data-2` engine과 catalog를 재현 배포하고 실제 `wasm-simd128` float64 수치 oracle,
120120
core 격리, process clone과 Machine image 이식을 닫았다.
121-
- 진행 중: NumPy부터 첫 실제 scientific package stack을 data profile에 넣는다. 이후
122-
`parallelProcesses`, `durableDisk` next를 순서대로 소진한다.
121+
- 완료: 공식 NumPy 2.5.1 sdist와 exact toolchain을 `data-3` engine의 13개 static module로 재현 빌드했다.
122+
array, dot, FFT, linalg, seeded random과 음성 경계, package clone과 Machine image 복원을 정식 gate로 닫았다.
123+
- 진행 중: `parallelProcesses.sharedMemoryThreads`의 현재 upstream capability를 정확히 판정한 뒤,
124+
`durableDisk.quotaEviction`, `localPythonParity.wasmToolLayer` 순서로 소진한다.
123125
- upstream이 열어 주는 thread와 dynamic linking은 capability detection과 exact failure로 받는다.
124126
- wasm 도구층을 먼저 넣고 Node guest는 같은 Machine lifecycle과 image 계약을 통과시킨다.
125127

@@ -369,8 +371,9 @@ implementation experience는 독립적이고 상호운용 가능한 구현, 저
369371
2,773,481 bytes, `sha256:297e22960319563421b9dcbed67dc7c43e42e456fcc01447ceb4de335ce5a236`다.
370372
- 설치 제품 실측: 공개 Control이 연 Edge에서 `six 1.17.0`을 설치하고 import했다. runtime은
371373
`wasi-0.0.0-wasm32`, `.cpython-314-wasm32-wasi.so`, `.abi3.so`, `.so`를 보고했다.
372-
- 현재 정확한 RED: NumPy 2.5.2는 허용 tag가 `py3-none-any`뿐인 core resolver에서
373-
`PYPROC_PACKAGE_RESOLUTION`로 멈춘다. screenshot은 922 x 920으로 직접 확인했고 SHA-256은
374+
- 당시 probe의 NumPy 2.5.2 요청은 배포되지 않은 좌표였으므로 native package reach의 증거가 아니었다.
375+
이 사실을 확인한 뒤 probe를 실제 배포된 2.5.1로 바로잡았다. 당시 안전 거절 화면은 922 x 920으로
376+
직접 확인했고 SHA-256은
374377
`16ce862b69982ce556a0b1a7c5e7bb4daa3d83775d2701e6a2982b8d7d7b1c0f`다.
375378
- gate 이빨: workspace 치환을 무력화한 음성 변형은
376379
`generated _sysconfigdata__wasi_wasm32-wasi.py does not expose a canonicalizable build root`로 RED였다.
@@ -416,9 +419,42 @@ implementation experience는 독립적이고 상호운용 가능한 구현, 저
416419
`wasm-simd128`이었다. float64 덧셈은 `[4,7,2,6,10]`, 내적은 `-4.75`다. 설치 gate는 core 격리,
417420
data process clone, Machine image 복원을 포함해 30/30 GREEN이고 별도 data browser gate는 10/10
418421
GREEN이다.
419-
- 대표 import 재측정: 같은 data engine에서 NumPy, SciPy, pandas, Polars는 모두
422+
- data-2 역사 기준선: 같은 data engine에서 NumPy, SciPy, pandas, Polars는 모두
420423
`ModuleNotFoundError`다. GREEN 화면이 성공과 한계를 함께 표시하며 직접 확인한 screenshot SHA-256은
421424
`aade1f5ea3478b8a68805712c2fe19259bfc8e44d5930141a7b694f1b8f8bb0b`다.
422-
- 다음 직렬 작업: exact source, license, reproducibility, size와 수치 oracle을 갖춘 첫 실제 scientific
423-
package stack을 NumPy부터 data profile에 넣고 네 import 경계를 다시 측정한다. 임의 PyPI native wheel
424-
지원은 먼저 주장하지 않는다.
425+
- NumPy source 계약: 공식 2.5.1 sdist
426+
`sha256:a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3`, Cython 3.1.2,
427+
Ninja 1.13.0과 sdist 내 Meson 1.11.1을 exact lock으로 고정했다. 13개 static module registry,
428+
중복, 누락과 금지 symbol 검사, deterministic archive와 wheel, config 경로 canonicalization을 build 계약에
429+
넣었다. 현재 C++ exception은 비활성이고 allocation 또는 PocketFFT invariant 위반은 abort하는 제한을
430+
manifest와 README에 공개했다.
431+
- build 중 첫 RED는 Windows embedded Python이 `PYTHONPATH`를 무시해 builder module을 찾지 못한 것이고,
432+
두 번째 RED는 Meson 자식 프로세스가 NumPy code generator 경로를 잃은 것이었다. 격리된 startup shim과
433+
자식 환경 복원으로 두 문제를 닫았다.
434+
- data-3 재현성: 두 격리 workspace의 선언 산출물 6개와 static archive가 byte-identical이다. engine은
435+
17,606,733 bytes,
436+
`sha256:42869426bd18a19004fe7244f2260144c4a217680f7dbcbc2c6302277826644e`, core 대비 증가는
437+
9,875,596 bytes다. NumPy wheel은 1,297,310 bytes,
438+
`sha256:c17a9e6ff30fd1371d6308a58142b04b6f6db52c3f233cfc7bf24bb44dca4cd0`, static archive는
439+
`sha256:fe19483eaa634550b648b261a9328bbe53931d40250f2d3a3c6956337121b5e5`다.
440+
- multi-wheel catalog: data catalog schema 3이 `pyproc-native-data==1.0.0``numpy==2.5.1`을 exact
441+
`data-3` engine에 함께 묶는다. catalog identity는
442+
`sha256:aa77504151ec7d69edb1f1fd614f96106893a4503594d70feea78c33d040e87a`다.
443+
- 상태 전이 RED: 설치 smoke 뒤 NumPy module을 지우면 static extension을 실제 import할 때 두 번째 초기화가
444+
`ImportError`로 멈췄다. 성공한 smoke import 자체를 commit 상태로 유지하고 실패 때만 이전 module과 path를
445+
복원하도록 고쳤다. 이어 package snapshot을 복제할 때 새 worker가 같은 static extension을 다시 초기화한
446+
문제는 package files를 먼저 재생하고 checkpoint memory를 나중에 복원해 닫았다.
447+
- 제품 증거: data browser gate 13/13, exact packed installed gate 33/33가 GREEN이다. 동일 environment를
448+
반복 설치해도 static module을 다시 초기화하지 않는 회귀 시험을 포함한다. NumPy 2.5.1의
449+
`sum=[3,12]`, `dot=32`, FFT 네 값 `(1+0j)`, `solve=[2,3]`, seeded random `[1,68,59,5,90]`
450+
실제 WASI에서 검증했다. SciPy, pandas, Polars는 모두 `ModuleNotFoundError`로 남는다. process clone과
451+
Machine image는 facade와 NumPy layer를 함께 복원한다.
452+
- 공개 Control 시각 증거: exact packed `pyproc-control`이 만든 GREEN 화면을 922 x 920 viewport에서 직접
453+
확인했다. screenshot은 162,736 bytes,
454+
`sha256:698ca96aeb85d96e150045b46751cbd1bd7f22ee6167e088d5eedc044b35b099`다. 첫 실행의 255.8초 중
455+
240초는 probe가 승리한 Promise 뒤 패배한 timeout timer를 취소하지 않은 잔류였고, 이를 고친 같은
456+
시나리오는 24.1초에 종료됐다.
457+
- 다음 직렬 작업: `parallelProcesses.sharedMemoryThreads`의 실제 플랫폼과 CPython 지원 경계를 음성 probe로
458+
고정한다. 지원되지 않으면 없는 능력을 우회해 주장하지 않고 그 좌표를 기록한 뒤
459+
`durableDisk.quotaEviction`으로 이동한다. 임의 PyPI native wheel 지원은 dynamic linking 전까지 주장하지
460+
않는다.

mainPlan/README.md

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

2020
현재 미완료 이니셔티브는 [9-agentComputerStandardReadiness](9-agentComputerStandardReadiness/README.md)다.
21-
M2의 hardware GPU 결과 oracle까지 완료했고 M3의 `packageReach`부터 직렬로 이어간다.
21+
M2의 hardware GPU 결과 oracle과 M3의 NumPy 2.5.1 static data profile까지 완료했다. 다음은
22+
`parallelProcesses.sharedMemoryThreads`의 실제 capability boundary부터 직렬로 이어간다.
2223

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

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"scripts/assetManifest.mjs",
6565
"scripts/assetCatalog.json",
6666
"scripts/engineBuilder",
67+
"scripts/scientificPackageBuilder",
6768
"scripts/nativePackageCatalog",
6869
"scripts/pyprocControl.mjs",
6970
"scripts/controlProtocolServer.mjs",

scripts/engineBuilder/buildOwnedEngine.mjs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url";
1010

1111
import { ownedBuildDetailsArguments, packageOwnedEngine } from "./packageOwnedEngine.mjs";
1212
import { nativeProfileBuildInput } from "./nativeProfileCompiler.mjs";
13+
import { buildOwnedNumpy, numpyMakeSyslibs } from "../scientificPackageBuilder/numpyStaticBuilder.mjs";
1314

1415
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
1516
const LOCK_PATH = join(SCRIPT_DIR, "engineBuildLock.json");
@@ -137,14 +138,37 @@ async function main() {
137138
], { cwd: targetBuildDir, env: targetEnv });
138139
run("make", ["--jobs", String(availableParallelism()), "all"], { cwd: targetBuildDir, env: targetEnv });
139140

141+
let scientificBuild = null;
142+
if (profileBuild.input.scientificPackages.length) {
143+
if (profileBuild.input.scientificPackages.length !== 1
144+
|| profileBuild.input.scientificPackages[0].name !== "numpy") {
145+
throw new Error(`owned ${profileName} scientific package recipe is unsupported`);
146+
}
147+
scientificBuild = await buildOwnedNumpy({ workspace: join(workspace, "scientific", "numpy"),
148+
cacheDir: downloads, cpythonSource: sourceDir, targetBuildDir, sdkDir,
149+
hostPython: join(nativeBuildDir, "python") });
150+
run("make", ["--jobs", String(availableParallelism()), "python.wasm",
151+
`SYSLIBS=${numpyMakeSyslibs(scientificBuild.archive)}`], { cwd: targetBuildDir, env: targetEnv });
152+
}
153+
140154
const oracle = run(wasmtime, ["run", "--wasm", "max-wasm-stack=16777216", "--dir", `${sourceDir}::/`,
141155
"--env", "PYTHONPATH=/Lib", join(targetBuildDir, "python.wasm"), "-c", profileBuild.input.oracle.code],
142156
{ capture: true });
143157
if (oracle.trim() !== profileBuild.input.oracle.stdout) throw new Error(`owned ${profileName} oracle failed: ${oracle}`);
158+
if (scientificBuild) {
159+
const scientific = profileBuild.input.scientificPackages[0];
160+
const scientificOracle = run(wasmtime, ["run", "--wasm", "max-wasm-stack=16777216",
161+
"--dir", `${sourceDir}::/`, "--dir", `${scientificBuild.layer}::/numpy-site`,
162+
"--env", "PYTHONPATH=/numpy-site:/Lib", join(targetBuildDir, "python.wasm"), "-c", scientific.oracle.code],
163+
{ capture: true });
164+
if (scientificOracle.trim() !== scientific.oracle.stdout) {
165+
throw new Error(`owned ${profileName} ${scientific.name} oracle failed: ${scientificOracle}`);
166+
}
167+
}
144168
const buildDetails = await ownedBuildDetailsArguments({ sourceDir, buildDir: targetBuildDir, target: lock.target });
145169
run(wasmtime, buildDetails.args);
146170
const packaged = await packageOwnedEngine({ sourceDir, buildDir: targetBuildDir, sdkDir, outDir,
147-
profileName, profileBuild });
171+
profileName, profileBuild, scientificBuild });
148172
console.log(`\nowned ${profileName} engine complete: ${JSON.stringify(packaged.outputs, null, 2)}`);
149173
}
150174

0 commit comments

Comments
 (0)