Skip to content

Commit fd28fc5

Browse files
committed
검증: check sandbox 지원 경계를 실제 실행으로 확정
상황: 기존 feasibility 검증기는 브라우저 격리와 Windows broker가 없다는 정적 문구만 반환해 어떤 check가 Web에서 강한 증거가 될 수 있는지, 어느 실패가 Local 전환 조건인지 증명하지 못했다. 현재 browser runtime은 외부 CDN core를 먼저 가져오며 launcher에는 AppContainer broker 구현이 없어서 계획 문구가 아니라 실제 엔진·운영체제·소스 상태를 함께 측정할 필요가 있었다. 변경: Chromium 149.0.7827.55, Firefox 151.0, WebKit 26.5에서 opaque-origin sandbox iframe을 엔진별 5회 생성해 origin, opener, storage, network, top navigation, WASM, blob module Worker, frame cleanup과 fresh counter를 검사했다. Windows 11 build 26200의 AppContainer·restricted token·Job Object API, 목표 Win10 22H2 build 19045 부재, launcher broker 파일 부재를 probe에 포함했다. pyproc pin과 25개 asset manifest SHA-256, core index와 cache/integrity 설정도 기록하고 browser output·variable strong subset, behavior localRequired, Local Windows·cold/warm offline unsupported 결정을 JSON SSOT로 고정했다. artifact ownership 생성물을 동기화하고 제품 계약과 학습 계획을 이 결정에 맞춘 뒤 완료된 05-check-sandbox-feasibility 계획과 부모 참조를 삭제했다. 영향: Web 후보 A는 connect-src none에서 network-first Python core를 부팅할 수 없어 채택되지 않으며 Chromium의 opaque frame blob module Worker 실패도 숨기지 않는다. 지원되지 않는 behavior와 Windows Local strong completion은 provisional executor 성공으로 승격되지 않고 후속 runtime enforcement 전까지 pending-implementation-workstream으로 남는다. 따라서 feasibility 자체는 실제 부정 결과까지 종료됐지만 구현되지 않은 보안 경계를 제품 지원으로 과장하지 않는다. 검증: uv run python -X utf8 tests/product/verifyCheckSandboxFeasibility.py 통과(completionEligible=true). Chromium p50/p95 78/79ms, Firefox 141/234ms, WebKit 125/141ms를 실측했다. 관련 pytest 23개가 통과했고 tests/run.py gate docs와 plan-quality, genProductContracts --check, 삭제 plan 참조 검색, git diff --check가 통과했다.
1 parent ced7811 commit fd28fc5

14 files changed

Lines changed: 374 additions & 91 deletions

File tree

contracts/artifactOwners.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ artifacts:
4646
surfacePaths:
4747
- landing/scripts/generateCurriculum.js
4848
- landing/src/lib/curriculumLessons.js
49+
- artifactId: check-sandbox-feasibility-decision
50+
role: source
51+
owner: learning-runtime
52+
sourcePath: contracts/checkSandboxFeasibilityDecision.json
53+
surfacePaths:
54+
- tests/product/checkSandboxCapabilityProbe.py
55+
- tests/product/verifyCheckSandboxFeasibility.py
56+
- mainPlan/astryx-product-experience/02-learning-method/README.md
4957
- artifactId: run-route-state
5058
role: source
5159
owner: product-architecture
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"browser": {
3+
"decision": "candidate-b-supported-subset",
4+
"localRequiredKinds": [
5+
"behavior"
6+
],
7+
"strongKinds": [
8+
"output",
9+
"variable"
10+
]
11+
},
12+
"localWindows": {
13+
"decision": "unsupported",
14+
"provisionalExecutorMayGrantStrongCredit": false
15+
},
16+
"offline": {
17+
"cold": "unsupported",
18+
"warm": "unsupported"
19+
},
20+
"enforcementState": "pending-implementation-workstream",
21+
"policyVersion": 1
22+
}

editor/src/lib/generatedContracts/artifactOwnership.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Generated by docs/skills/ops/tools/genProductContracts.py.
22
// Source SHA-256: 4679b9c14619d69672ff06da0291dd45c71b100ee6e8a0b5698fb96cd55eb113
3-
// Owners SHA-256: 61dca3bfb4a3f454eb60e94656be43b240fc7722c23bdc7876e0bc9b658dbdee
3+
// Owners SHA-256: 4bd5c45f4928736c6026a0770946a8fcf60ad8a5671e2e57ba1a72a5890e6857
44
export const ARTIFACT_OWNERSHIP_CONTRACT_SHA256 = "4679b9c14619d69672ff06da0291dd45c71b100ee6e8a0b5698fb96cd55eb113" as const;
5-
export const ARTIFACT_OWNERSHIP_OWNERS_SHA256 = "61dca3bfb4a3f454eb60e94656be43b240fc7722c23bdc7876e0bc9b658dbdee" as const;
5+
export const ARTIFACT_OWNERSHIP_OWNERS_SHA256 = "4bd5c45f4928736c6026a0770946a8fcf60ad8a5671e2e57ba1a72a5890e6857" as const;
66

77
export type ArtifactRole = "source" | "generated" | "packaged" | "evidence";
88

launcher/codaro-launcher/src/generated_contracts/artifact_ownership.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// Generated by docs/skills/ops/tools/genProductContracts.py.
22
// Source SHA-256: 4679b9c14619d69672ff06da0291dd45c71b100ee6e8a0b5698fb96cd55eb113
3-
// Owners SHA-256: 61dca3bfb4a3f454eb60e94656be43b240fc7722c23bdc7876e0bc9b658dbdee
3+
// Owners SHA-256: 4bd5c45f4928736c6026a0770946a8fcf60ad8a5671e2e57ba1a72a5890e6857
44
use serde::{Deserialize, Serialize};
55

66
pub const ARTIFACT_OWNERSHIP_CONTRACT_SHA256: &str = "4679b9c14619d69672ff06da0291dd45c71b100ee6e8a0b5698fb96cd55eb113";
7-
pub const ARTIFACT_OWNERSHIP_OWNERS_SHA256: &str = "61dca3bfb4a3f454eb60e94656be43b240fc7722c23bdc7876e0bc9b658dbdee";
7+
pub const ARTIFACT_OWNERSHIP_OWNERS_SHA256: &str = "4bd5c45f4928736c6026a0770946a8fcf60ad8a5671e2e57ba1a72a5890e6857";
88

99
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1010
#[serde(rename_all = "camelCase")]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Generated by docs/skills/ops/tools/genProductContracts.py.
22
// Source SHA-256: 4679b9c14619d69672ff06da0291dd45c71b100ee6e8a0b5698fb96cd55eb113
3-
// Owners SHA-256: 61dca3bfb4a3f454eb60e94656be43b240fc7722c23bdc7876e0bc9b658dbdee
3+
// Owners SHA-256: 4bd5c45f4928736c6026a0770946a8fcf60ad8a5671e2e57ba1a72a5890e6857
44
pub mod artifact_ownership;

mainPlan/astryx-product-experience/00-product-contract/01-prd-improvement-loop/05-check-sandbox-feasibility/README.md

Lines changed: 0 additions & 68 deletions
This file was deleted.

mainPlan/astryx-product-experience/00-product-contract/01-prd-improvement-loop/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,12 @@ Product evidence maturity는 `E0 없음`, `E1 synthetic`, `E2 vertical slice`, `
3535
| --- | --- | --- |
3636
| 03 | [learning-vertical-slice](03-learning-vertical-slice/) | ID뿐인 check, 472 일괄 범위, 확인 클릭 마찰 |
3737
| 04 | [evidence-migration](04-evidence-migration/) | canonical writer 전환 뒤 downgrade 진도 손실 |
38-
| 05 | [check-sandbox-feasibility](05-check-sandbox-feasibility/) | 검증되지 않은 browser·Windows sandbox 결정 |
3938
| 06 | [astryx-journey-evidence](06-astryx-journey-evidence/) | 렌더 0, Local viewport 모순, font·AT·proof media 공백 |
4039
| 07 | [release-research-operations](07-release-research-operations/) | `/app/` release 순서와 720명 단일 blocker |
4140
| 08 | [r10-independent-review](08-r10-independent-review/) | remediation 작성자 재채점, 이전 점수 노출, 낮은 round 덮어쓰기 |
4241
| 09 | [learning-quality-revalidation](09-learning-quality-revalidation/) | 부분 browser pass를 전체 통과로 오인, strong·전이·지연 검색의 전수 격차 |
4342

44-
평가 계약은 `contracts/`에 있다. 04가 downgrade-safe rollback을 구현하고, 05 feasibility가 허용한 범위에서 03·06과 top-level 제품 경로의 W0 증거를 만든다. 09는 Day 1 전이·검색과 W0 Local parity를 다시 감사해 08의 독립 평가 입력을 갱신한다. 현재 source는 R10 전에 468레슨의 strong assessment와 472개 public route까지 확장됐다. 이 구현을 숨기거나 되돌리지는 않지만, machine source coverage는 승인된 W1, E3, 사람 content review나 종료 조건 검증이 아니다. R10 미실시는 추가 machine 개선을 멈추는 조건이 아니라 독립 품질과 공개 승격을 주장하지 못하게 하는 판정 경계다. 07은 candidate release와 경로별 효능 승격을 분리하고, 03~07·09 closure evidence가 없는 상태에서는 R10 input을 seal하지 않는다.
43+
평가 계약은 `contracts/`에 있다. 04가 downgrade-safe rollback을 구현하고, `contracts/checkSandboxFeasibilityDecision.json`허용한 범위에서 03·06과 top-level 제품 경로의 W0 증거를 만든다. 09는 Day 1 전이·검색과 W0 Local parity를 다시 감사해 08의 독립 평가 입력을 갱신한다. 현재 source는 R10 전에 468레슨의 strong assessment와 472개 public route까지 확장됐다. 이 구현을 숨기거나 되돌리지는 않지만, machine source coverage는 승인된 W1, E3, 사람 content review나 종료 조건 검증이 아니다. R10 미실시는 추가 machine 개선을 멈추는 조건이 아니라 독립 품질과 공개 승격을 주장하지 못하게 하는 판정 경계다. 07은 candidate release와 경로별 효능 승격을 분리하고, 03·04·06·07·09 closure evidence가 없는 상태에서는 R10 input을 seal하지 않는다.
4544

4645
## TODO 삭제 조건
4746

mainPlan/astryx-product-experience/00-product-contract/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
| 상주 스케줄, 웹훅, GUI 자동화 | 미지원 | 지원 | Local의 핵심 가치로 전면 배치 |
2222
| 진행 이어하기 | 브라우저 저장소, 내보내기 가능 | 로컬 저장소 | 같은 schema와 merge 규칙 사용 |
2323

24-
`browser-supported`는 lesson label이 아니라 check sandbox feasibility, package/runtime capability와 browser matrix를 통과한 `CheckSpec` 단위 판정이다. 지원되는 레슨은 Web에서 읽기·수정·실행·strong check·진도 저장까지 끝나며 다운로드 확인을 요구하지 않는다. 미지원 check를 `noError`로 바꾸지 않고 정확한 이유와 Local handoff를 제공한다. 현재 Python core 공급망은 network-first이므로 첫 Run에 network가 필요하고 offline 학습을 출시 약속으로 쓰지 않는다.
24+
`browser-supported`는 lesson label이 아니라 check sandbox feasibility, package/runtime capability와 browser matrix를 통과한 `CheckSpec` 단위 판정이다. `contracts/checkSandboxFeasibilityDecision.json`의 현재 판정은 browser `output`·`variable`만 supported subset이고 `behavior``localRequired`, Windows Local release strong completion은 `unsupported`다. runtime enforcement가 구현되기 전 provisional executor 성공을 release strong credit으로 승격하지 않는다. 미지원 check를 `noError`로 바꾸지 않고 정확한 이유와 Local handoff를 제공한다. 현재 Python core 공급망은 network-first이므로 첫 Run에 network가 필요하고 offline 학습을 출시 약속으로 쓰지 않는다.
2525

2626
## Lesson identity 계약
2727

mainPlan/astryx-product-experience/02-learning-method/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ Web에서 만든 notebook, evidence, path position을 Local로 가져와 실제
128128

129129
lesson YAML의 `assessment``outcomeClaims`, `performanceClaim`, fixture, positive·negative predicate, misconception case, support policy, acquisition·near/far transfer·delayed retrieval variant, tier parity와 reviewer approval을 직접 소유한다. content ledger는 `assessmentHash`와 approval evidence만 참조한다. 현재 468레슨에 source 계약과 실행 검산이 있지만 이것을 [learning vertical slice](../00-product-contract/01-prd-improvement-loop/03-learning-vertical-slice/)의 사람 검수나 독립 승인으로 대체하지 않는다. 내용 없는 ID 생성과 동일 문제 복제는 완료 근거가 아니다.
130130

131-
아래 `CheckSandboxPolicy@1`후보 설계다. [sandbox feasibility packet](../00-product-contract/01-prd-improvement-loop/05-check-sandbox-feasibility/)이 browser 3-engine과 Win10 probe, 보안 negative fixture, boot·cleanup p95를 통과한 뒤에만 확정한다. 실패 시 일반 subprocess로 약화하지 않고 지원 check subset과 `localRequired` 경계를 다시 결정한다.
131+
`contracts/checkSandboxFeasibilityDecision.json`browser 3-engine과 현재 Windows capability probe의 확정 판정이다. Chromium·Firefox·WebKit의 opaque frame은 origin·storage·network·top navigation·opener·WASM·frame cleanup을 통과했지만 Chromium blob module Worker와 network-first Python core 공급이 실패했다. 따라서 browser는 후보 B를 선택해 `output`·`variable`만 strong subset으로 남기고 `behavior``localRequired`로 분류한다. 현재 Windows 11 build 26200에는 AppContainer·restricted token·Job Object API가 있지만 목표 Win10 22H2 실측과 launcher broker 구현이 없어 Local release strong completion은 `unsupported`다. cold·warm offline도 지원하지 않는다. 이 판정의 runtime enforcement는 아직 `pending-implementation-workstream`이며, 적용 전 provisional executor 성공을 release strong credit으로 승격하지 않는다.
132132

133-
`CheckSandboxPolicy@1` 후보는 다음과 같다.
133+
확정 판정의 후속 구현 계약은 다음과 같다.
134134

135135
- browser runtime provider는 현재 editor가 이미 고정한 `pyproc` 하나뿐이다. 학습자 셀은 `browserPythonRuntime.ts`의 long-lived singleton에서 실행해 `ObservedRun`만 만들고, strong property·behavior check는 같은 mutable namespace를 절대 재사용하지 않는다. `BrowserCheckExecutor`는 제출 source, 직렬화 fixture, artifact descriptor만 새 check sandbox에 넘기며 expected code나 expected artifact를 학습자 singleton에 주입하지 않는다. sandbox 후보는 pyproc의 공개 `boot`·`Runtime`·`EngineContract` 뒤에서 fresh isolated worker를 만드는 adapter다. 제품 코드가 Pyodide를 직접 import·boot하거나 별도 package/install state를 만들지 않는다. 현재 pyproc 공개 API로 opaque frame 내부 worker boot가 불가능하면 pyproc에 versioned isolated-worker entry를 먼저 추가해 pin을 갱신하거나, 해당 check를 `localRequired`로 분류한다. 두 번째 browser Python engine은 fallback이 아니다.
136136
- browser transport 후보는 opaque-origin `<iframe sandbox="allow-scripts">`, generated CSP bootstrap, 전용 `MessagePort`다. host는 SRI 검증한 pyproc module graph와 engine 설정을 전달하되, opaque origin에서 module graph·worker·WASM을 실제로 부팅할 수 있는지는 feasibility packet이 결정한다. `check-sandbox.html`은 외부 import가 없는 최소 inline bootstrap 하나만 가지며 `buildCheckSandbox.mjs`가 그 exact bytes의 SHA-256을 CSP `default-src 'none'; connect-src 'none'; worker-src blob:; script-src 'sha256-<generated>' blob: 'wasm-unsafe-eval'`과 `checkSandboxBootstrapHash.ts`에 함께 생성한다. Local의 동적 `127.0.0.1:<port>`를 지원하려고 host는 runtime `location.origin`의 base64url과 256-bit nonce를 iframe URL fragment에 넣고 같은 값을 최초 message에 보낸다. fragment는 HTTP request·referrer에 보내지 않으며 bootstrap hash를 바꾸지 않는다. 최초 `window.message`에서만 `event.source===parent`, `event.origin===fragment hostOrigin`, fragment/message nonce 일치, schema version을 검사한 뒤 port를 한 번 bind한다. fragment 제거는 `history.replaceState`를 try/catch하고 opaque-origin `SecurityError`면 실패를 허용하되 parsed origin·nonce 변수와 raw fragment buffer를 즉시 null/zeroize하며 console·telemetry에 쓰지 않는다. iframe은 run 종료 때 폐기한다. `MessagePort` 후속 message에는 origin이 없으므로 origin을 주장하지 않고 frame nonce, monotonic decimal request sequence, unique request ID, payload schema를 검사해 replay·out-of-order를 거부한다. bootstrap은 blob Worker만 만들며 hash mismatch·추가 script·`unsafe-inline`은 build gate가 거부한다. `allow-same-origin`, form, popup, download, top navigation, storage access를 주지 않고 host는 versioned RPC와 1MB output만 받는다. fresh namespace, WASM linear memory maximum 256MB, virtual FS 32MB, wall timeout 8초를 넘기면 iframe과 worker를 함께 폐기한다.

src/codaro/generatedContracts/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Generated by docs/skills/ops/tools/genProductContracts.py.
22
# Source SHA-256: 4679b9c14619d69672ff06da0291dd45c71b100ee6e8a0b5698fb96cd55eb113
3-
# Owners SHA-256: 61dca3bfb4a3f454eb60e94656be43b240fc7722c23bdc7876e0bc9b658dbdee
3+
# Owners SHA-256: 4bd5c45f4928736c6026a0770946a8fcf60ad8a5671e2e57ba1a72a5890e6857
44
from .artifactOwnership import (
55
ARTIFACT_OWNERSHIP_CONTRACT_SHA256,
66
ARTIFACT_OWNERSHIP_OWNERS_SHA256,

0 commit comments

Comments
 (0)