# 자동화 경로와 완료 확인 [색인](/llms.txt) · [핵심 개념](/docs/agents/concepts.md) 자동화 입력은 [Flow 1.0 JSON Schema](/schema/flow-1.0.json)와 [작은 oxide-window 예제](/docs/agents/examples/oxide-window.flow.json)를 참고하세요. 데이터 형식의 구조 검사와 앱의 정준 의미 검증은 별도이며, 지원 범위는 [Flow 계약 설명](/docs/agents/concepts.md#자동화용-flow-10)을 따릅니다. ## 브라우저 API 1.0 브라우저의 `window.arpelierApps`는 개발용 내부 객체입니다. `History.captureSnapshot()`, `restoreSnapshot()`, 개별 모델의 상태 변경이나 `.rail-dot` 클릭을 안정된 공개 API로 간주하지 마세요. 내부 객체의 이름·저장 형식·비동기 완료 시점은 공개 버전 계약이 아닙니다. 앱이 초기화되면 `window.arpelier`에 공개 경계가 설치됩니다. `version === "1.0"`과 `schema === "https://arpelier.com/schema/flow-1.0.json"`을 먼저 확인하세요. 모든 메서드는 Promise를 반환합니다. API는 현재 열린 프로젝트를 대상으로 하며 로그인·접근 권한을 우회하지 않습니다. 다른 배포 버전에서 객체나 메서드가 없으면 있다고 가정하지 마세요. | 메서드 | 결과와 범위 | | --- | --- | | `getFlow()` | 현재 문서의 복사본을 Flow 1.0 형식으로 반환. 내부 배열을 직접 노출하지 않음 | | `validate(flow?)` | 생략 시 현재 문서. `{ valid, document, authoringValid, authoringValidationScope, executionReadiness, issues, flow?, warnings, warningsScope, warningCoverage }`. 외부 입력은 프로젝트를 열기 전에도 검증 가능 | | `setFlow(flow, { agent? })` | 전체 검증 후 Undo 한 단계로 적용. `{ applied, undoable, status }`. 자동 계산·저장은 하지 않음. `agent`는 [AI 생성 기록](#ai-생성-기록)에 남길 AI 이름 | | `appendFlow({ processes, masks?, materials? }, { expectedLastStepId?, agent? })` | 현재 마지막 단계 뒤에 공정(과 필요한 새 마스크·재료)을 덧붙임. 기존 단계는 바꾸지 않으며 합친 문서를 전체 검증 후 Undo 한 단계로 적용. 본딩 단계 뒤에도 쓸 수 있음. `{ applied, undoable, appendedStepIds, lastStepId, status }`. 자동 계산·저장은 하지 않음 (`api.capabilities?.appendFlow` 확인) | | `getView()` | 현재 보기 상태: `mode`(`3d`·`cutaway`·`slice2d`), `cutaway`(잘라낸 상자, nm), 투명 재료, 투명도, 강조 재료, 카메라(방위각·고도각·목표점·거리·시야각), 표면(`surface`), 조명(`lighting`), 캔버스 표시(`overlays`), 영역 크기, 그리기 비용(`render`, 버전 1.2부터, 툰 윤곽선 시간은 1.4부터) | | `setView({ transparentMaterialIds?, transparentOpacity?, highlightMaterialIds?, cutaway?, camera?, surface?, lighting?, overlays? })` | 보여주기 전용. 재료 투명화, 3D 단면(잘라낼 상자), 카메라, 표면 스타일, 조명·그림자, 바닥 그리드·좌표축·눈금자를 한 번에 적용하고 `getView()` 결과를 돌려줌. 공정·계산은 바꾸지 않음. 프로젝트를 저장하면 함께 저장됨 (`api.capabilities?.view` 확인, 버전 1.1부터 표면·조명·표시) | | `getAppearance()` | 재료별 겉모습: 색, 종류(`family`), 표면 프리셋과 고를 수 있는 프리셋 목록, 금속성, 거칠기, 투명할 때의 불투명도, 구조에서 차지하는 부피 비율(`volumeFraction`) | | `setAppearance({ materials: [{ id, color?, metalness?, roughness?, surfacePreset?, transparentOpacity? }] })` | 재료 패널과 같은 겉모습 설정을 바꿈. 한 번의 실행 취소 단위이고 프로젝트를 저장하면 함께 저장됨. 형상·재료 종류·계산 결과는 바꾸지 않음 (`api.capabilities?.appearance` 확인) | | `captureView({ supersample? })` | 현재 3D 화면을 PNG data URL로 반환 `{ dataUrl, width, height, supersample }`. 기본은 화면 밀도의 2배로 그린 뒤 화면 크기로 줄인 고품질 정지 화면(가장자리가 매끄럽고 얇은 막이 덜 끊김). `supersample: 1`은 화면 그대로. GPU 한도를 넘으면 배율을 낮추고 실제 배율을 돌려줌 (`capabilities.view.capture`) | | `computeTo(stepId?, options?)` | authored 공정 ID 또는 기본 `"final"`까지 실제 완료를 확인하고 `{ computed, computedUpTo, validationScope, flowValid, warnings, warningCoverage, status }` 반환 | | `status()` | 현재 계산·저장·격자·메모리 관측. 계산이나 저장을 시작하지 않음 | | `getCrossSection({ axis, at, dz? })` | 현재 완료된 선택 단계에서 재료 ID의 2차원 배열을 반환 | | `inspectStructure({ checks })` | 현재 완료된 선택 단계의 기하학적 연결성·공유면 접촉 면적을 읽기 전용으로 측정 | | `prepareBonding(request, options?)` | 현재 완료된 최종 단계 뒤의 본딩 초안을 준비. 공정 메모(`note`)와 본딩을 감쌀 모듈 그룹(`group`)을 함께 지정할 수 있음. 문서·선택·계정 저장 무변경 | | `applyBonding({ token, acceptSourceDisclosure? }, options?)` | 검토한 추가·갱신 초안을 Undo 한 단계로 적용하고 `{ stepId, lastStepId }`를 반환. 자동 계산·저장 없음 | | `cancelBonding({ token })` | 준비된 초안을 버리고 보유 자료 해제 | | `computeBonding(stepId, options?)` | 적용 또는 재검증 승인된 본딩 단계까지 기존 계산 경로 실행 | | `resumeBonding({ stepId, compareSource? }, options?)` | 저장된 고정본의 자산·재료·배치를 격리 검증한 뒤 현재 세션의 계산 승인. 문서·선택 무변경 | | `prepareBondingUpdate({ stepId, placement?, acceptSourceDisclosure? }, options?)` | 기존 본딩에 복사할 최신 원본을 따로 준비·검토. 같은 `applyBonding`으로 명시 적용 | | `exportStructure({ format?, scaleMode?, includeHidden?, steps?, materialInfo?, deliver? })` | 앱의 내보내기 메뉴와 같은 규칙으로 3D 구조 파일(GLB·3MF·STL)을 만듦. 여러 공정 단계를 한 파일로 묶을 수 있고 재료 정보를 함께 넣음. 공정·보기·저장 상태는 바꾸지 않음 ([3D 파일로 내보내기](#3d-파일로-내보내기), `api.capabilities?.export` 확인) | | `save()` | 기존 계정 저장 경로 실행. `{ saved, savedAt, projectId, saveState }`로 결과 확인 | ```js const api = window.arpelier; if (api?.version !== "1.0") throw new Error("Arpelier API 1.0 is unavailable"); const current = await api.getFlow(); const next = structuredClone(current); // next의 요청된 필드만 수정합니다. 사용자 이름·메모와 유지할 ID를 보존하세요. const report = await api.validate(next); if (!report.valid) throw new Error(JSON.stringify(report.issues)); await api.setFlow(report.flow); const computed = await api.computeTo("final", { timeoutMs: 600_000, onProgress: progress => console.log(progress), }); const state = await api.status(); const section = await api.getCrossSection({ axis: "y", at: next.domain.LY / 2, dz: 1, }); // 저장이 요청되었고, 로그인 및 편집 권한이 있는 프로젝트에서 실행합니다. const saved = await api.save(); if (!saved.saved) throw new Error("Account save did not complete"); ``` `setFlow`는 현재 워크스페이스의 화면 설정·분할 상태를 보존하면서 문서를 바꿉니다. v1 미지원 BONDING/PARTICLE 등이 포함된 기존 프로젝트도 조용히 지우지 않고 거부합니다. 계산·저장·복원·Motion 전환 중에는 변경 작업이 `BUSY`로 실패합니다. 이 API는 프로젝트 생성·열기, 자동 로그인, Save as, 공유 권한 변경을 제공하지 않습니다. Flow의 rect/circle/polygon 마스크 도형에서 생략한 `fill`·`stroke`는 검증·적용·읽기 시 저장 코덱과 같은 극성별 기본색으로 채워집니다. 도형 좌표·극성과 계산 해상도는 바꾸지 않습니다. 명시한 색은 Flow 읽기·적용에서 유지하지만, 기존 계정 저장 코덱은 사용자 지정 색을 보존하지 않으므로 사용자 지정 색의 저장·재열기 동일성까지 보장하지 않습니다. ## 검증 결과 읽기 `validate()`는 독립적으로 확인할 수 있는 오류를 `issues`에 함께 반환합니다. 각 항목의 `level`, `code`, `path`, `message`를 읽고, 중복 ID 오류의 `relatedPaths`가 있으면 최초 위치도 함께 확인하세요. 기존 오류 코드는 유지되므로 대소문자를 임의로 정규화하지 마세요. `$.processes[2].thickness`처럼 배열 위치·필드가 가리키는 입력을 수정하세요. 잘못된 상위 구조에 의존하는 검사는 건너뛸 수 있으므로 오류를 고친 뒤 다시 검증해야 합니다. JSON으로 표현할 수 없는 입력은 즉시 거부될 수 있습니다. `appendFlow`는 사용자가 보는 앞에서 모듈 단위로 구조를 쌓을 때 사용합니다. 앞 단계를 다시 보내지 않으므로 이미 작성된 공정·메모·ID를 실수로 바꾸지 않습니다. 덧붙인 뒤 `computeTo(lastStepId)`로 그 모듈까지 계산하면, 앞 모듈의 계산 결과(같은 앞부분의 캐시)를 이어서 사용할 수 있습니다. `expectedLastStepId`를 주면 그사이 사용자가 문서를 바꿨을 때 `FLOW_CHANGED`로 거부합니다. 이미 있는 공정 ID, 내용이 다른 같은 ID의 마스크·재료, `processes`·`masks`·`materials` 외의 키(도메인·이름 등)는 `INVALID_FLOW`로 거부하며 문서를 바꾸지 않습니다. 같은 내용의 마스크·재료를 다시 보내는 것은 허용됩니다. 도메인·기존 단계의 수정은 `setFlow`를 사용하세요. ```js await api.appendFlow({ processes: [ { id: "liner", kind: "OXIDATION", thickness: 6 }, { id: "fill", kind: "DEPO", material: 18, thickness: 300 }, ] }, { expectedLastStepId: "strip" }); await api.computeTo("fill"); ``` `validate()`와 `validate(null)`의 `document`는 `"current"`, 명시한 입력의 검증은 `"input"`입니다. `setFlow()`가 문서 검증으로 거부되면 오류의 `document`와 `errors[]`를 함께 읽으세요. 각 상세 오류의 `code`·`path`·`message`와 선택적인 `relatedPaths`는 해당 문서 기준입니다. 짧은 안내에는 제공된 `summary`를 사용하세요. 문서 검증 오류에서는 첫 상세 오류를 담습니다. 기존 최상위 `message`와 오류 코드는 호환성을 위해 유지합니다. 기존 최상위 오류가 포괄적인 경로를 가리킬 때도 상세 배열에서 실제 수정할 필드를 확인할 수 있습니다. 현재 문서·선택·프로젝트·세션·계정 문맥이 유지되는 동안에는 같은 상세 정보가 `status().lastError`에도 남습니다. 입력이 유효해도 현재 문서에 보호할 미지원 내용이나 잘못된 설정이 있으면 계속 거부하므로, 현재 문서를 UI에서 수정하거나 Undo한 뒤 다시 시도하세요. `valid: false`일 때는 적용 가능한 `flow`가 반환되지 않습니다. 오류 수집은 입력을 수정하거나 계산하지 않으며, `setFlow()`는 문서 전체에 오류가 하나라도 남으면 계속 거부합니다. `valid: true`도 실제 계산·물리 타당성·계정 저장 성공을 뜻하지 않습니다. ## 작성 검증과 현재 실행 준비 상태 `valid`와 `authoringValid`는 같은 Flow 1.0 작성 검증 결과이며 `authoringValidationScope`는 `"flow-1.0"`입니다. 본딩이 있는 문서는 이 값이 false여도 전용 경로로 실행할 수 있습니다. `validate()`와 `status()`의 `executionReadiness`를 별도로 읽으세요. - `state: "ready"`: 현재 선택된 `targetStepId`가 기존 실행 사전 검사를 통과함. 계산 완료·자산 재검증·계정 저장을 뜻하지 않습니다. - `state: "resume-required"`: 해당 범위의 고정 본딩에 현재 세션의 승인이 필요함. `resumeBonding()`의 실제 검증이 성공해야 하며 손상된 자산도 성공한다고 보장하지 않습니다. - `state: "blocked"`: 실행 중이거나 입력·권한·지원 범위 문제로 현재 실행할 수 없음. `code`와 `summary`를 확인하고, 제공된 경우 `document`와 `errors[]`를 함께 읽습니다. 실행 중 상태처럼 문서 검증 외의 원인에는 문서·공정별 오류가 없을 수 있습니다. 본딩의 재개·자산 오류는 일반 Flow 작성 오류와 구분됩니다. - `state: "unavailable"`: 열린 프로젝트가 없거나 검증 입력이 현재 문서와 달라 실행 상태를 연결할 수 없음. 계산을 시작할 수 없는 이유가 `state: "blocked"`, `code: "BUSY"`라면 문서 오류로 단정하거나 같은 요청을 반복하지 마세요. `status()`의 `computing`·`operation`·`saveState`와 화면의 진행 상황·대화상자를 확인합니다. 진행 중 작업의 완료·취소 또는 필요한 대화상자 처리 후 `status()`와 현재 문서의 `validate()`로 프로젝트·`flowRevision`·`targetStepId`와 차단 원인을 다시 확인하세요. Motion을 사용 중이거나 원래 화면 복원 실패가 표시되면 공정 영역을 직접 선택해 복원을 다시 시도하고 결과를 확인하세요. 복원에 다시 실패하면 표시된 오류를 먼저 해결해야 합니다. `computing: false`와 `operation: null`만으로 재시도 가능하다고 판단하지 마세요. 잠금이 풀려도 다른 오류가 남으면 그 원인을 해결하고, 이미 완료된 작업은 중복 실행하지 않습니다. `BUSY`가 모두 시간 경과만으로 해소되는 것은 아닙니다. 이 관측은 선택을 바꾸거나 본딩을 자동 재개하거나 원본을 조회하지 않습니다. `status().validationScope`는 **현재 선택의 실행 사전 검사 범위**이며 최종 단계의 완료 결과가 아닙니다. 실제 요청한 계산 범위는 `computeTo()` 응답에서 확인하세요. 실행 시점에는 기존 검사를 다시 수행합니다. `status()`는 `authoringValid`, 같은 의미의 `flowValid`, `flowRevision`, `lastOperation`, `lastError`를 추가로 제공합니다. `lastOperation`의 작업명·요청 단계·실제 단계·시각·문서 표식과 `current`를 함께 읽으세요. 같은 프로젝트·세션·계정에서 문서나 선택이 바뀌면 이전 작업은 `current:false`, `lastError:null`입니다. 프로젝트·작업 세션·계정이 바뀌면 이전 작업의 상세를 노출하지 않도록 `lastOperation`과 `lastError`가 모두 `null`입니다. `lastError.scope: "operation-request"`는 특정 요청 실패이며 현재 문서 자체가 잘못됐다는 뜻이 아닙니다. `flowRevision`은 로컬 비교용 표식으로 영구 서버 리비전이나 암호학적 내용 해시가 아닙니다. ## 예전 프로젝트의 불필요 필드 정리 저장 프로젝트를 로드·저장하거나 `getFlow()`로 복사할 때, 현재 공정에서 계산에 쓰이지 않는다고 확인된 필드만 정리합니다. 대상은 STRIP/CMP의 `materialetch`, ALD의 `conformality`, 다른 공정에 남은 WET 전용 옵션과 식각·단일 재료 선택 모드입니다. 각 옵션을 실제로 사용하는 공정의 값과 사용자 이름·메모·ID는 보존합니다. 알 수 없는 필드·공정·자산을 일괄 삭제하거나 STRIP의 옛 `materialetch`를 새 식각 대상으로 바꾸지 않습니다. 이미 열려 있는 프로젝트는 아래 순서로 복구할 수 있습니다. `getFlow()`와 `validate()`는 원본·히스토리·계정 저장을 바꾸지 않습니다. 적용은 `setFlow()`의 Undo 한 단계이며, 계산과 계정 저장은 별도입니다. ```js const recovered = await api.getFlow(); const check = await api.validate(recovered); if (!check.valid) throw new Error(JSON.stringify(check.issues)); await api.setFlow(check.flow); ``` 새 Flow 입력은 계속 엄격하게 검증하므로 불필요한 옛 필드를 새 문서에 넣지 마세요. 정리 후에도 남는 알 수 없는 필드, 활성 미지원 모델, BONDING/PARTICLE 자원이 있으면 이 경로는 거부됩니다. 해당 공정의 지원 경로나 UI를 사용하고, 검증을 통과시키려고 의미 있는 설정을 임의로 삭제하지 마세요. ## 공정 종류 변경과 빈 초안 복구 Inspector에서 공정 종류를 바꾸면 이전 종류의 알려진 전용 필드 중 새 종류가 사용하지 않는 필드를 정리합니다. 예를 들어 STRIP의 `liftOff`, ALD의 `ald*`, PHOTO의 광학 옵션은 다른 종류로 넘어가지 않습니다. ID·이름·메모·비활성화 상태와 공통 메타데이터는 유지하며 Undo/Redo로 전환 전후를 복원할 수 있습니다. 재료·마스크·두께 중 실제로 초기화된 항목은 비차단 안내에 표시됩니다. 현재 프로젝트에서는 Undo 단축키도 안내합니다. 공정마다 설정의 의미가 다르므로 새 종류의 재료·대상과 두께를 다시 확인하세요. 이 규칙은 명시적인 종류 변경에만 적용됩니다. 기존 저장 문서 전체를 공개 작성 스키마로 필터링하는 기능은 아닙니다. 현재 문서에 UI가 만든 기본값 그대로의 빈 `NEW` 단계만 추가되어 있어도, 검증을 통과한 완전한 문서를 `setFlow()`로 적용할 수 있습니다. 새 입력은 여전히 전체 검증하며 적용 전 문서는 Undo로 복원됩니다. 이름·메모·변경된 설정·알 수 없는 필드가 있는 `NEW`, 중복 ID, 잘못된 공통 자원이나 미지원 자산은 이 예외로 버리지 않습니다. `validate()`와 `computeTo()`의 상세 NEW 진단도 미완성 초안임을 알리고 지원되는 공정 종류와 필수 항목을 완료하도록 안내합니다. 외부 입력은 해당 Flow를 수정하고, 열린 문서는 Inspector에서 내용을 보존하면서 완성하세요. `setFlow()`가 내용 있는 현재 NEW를 거부할 때에는 자동 교체로부터 보호한다는 설명을 추가합니다. 외부 입력 검증이나 계산 거부 자체가 현재 문서 교체 보호를 뜻하는 것은 아닙니다. `validate()`가 빈 `NEW`를 실행 가능한 공정으로 인정하는 것은 아닙니다. ## 계산 완료와 오류 뒤 단계에 검증 오류가 있어도 **명시한 공정 ID**까지의 계산을 요청할 수 있습니다. 이 부분 검증 경로는 문서 전체에 그룹·COMPOSITE가 없고 모든 공정 ID가 안정적인 고유 문자열인 경우에만 사용합니다. 전체 공통 도메인·재료·마스크와 요청 단계까지의 모든 공정이 검증을 통과해야 합니다. 공통 자원 또는 앞선 공정의 오류는 계속 계산을 막습니다. 원본 문서를 자르거나 뒤 단계의 오류를 고치지 않으며 계산 선택만 바꿉니다. `computeTo()`나 `computeTo("final")`은 일반 Flow 전체 검증을 유지합니다. 그룹이 있는 문서도 전체 검증이 필요합니다. 요청 범위에 BONDING이 있으면 기존의 명시적 적용·재개 승인과 고정 자산 검증 경로를 따릅니다. 목표 뒤에만 있는 BONDING의 원본을 조회해 부분 계산을 허가하는 방식은 사용하지 않습니다. 작성 오류로 계산이 거부되면 `document: "current"`와 상세 `errors[]`에서 각 공정의 경로를 확인하세요. 같은 문장이라도 경로가 다르면 별개의 오류입니다. `summary`는 첫 오류의 짧은 안내이며 전체 오류 목록을 대체하지 않습니다. 유효한 접두부 계산이 허용된 경우에는 뒤 단계 오류를 계산 실패로 보고하지 않습니다. 성공 결과의 `computedUpTo`와 함께 다음 필드를 확인하세요. | `validationScope` | `flowValid` | 확인된 범위 | | --- | --- | --- | | `"whole-flow"` | `true` | 전체 문서가 Flow 1.0 검증을 통과함 | | `"flat-prefix"` | `false` | 공통 자원과 명시한 단계까지의 평면 공정이 검증됨. 전체 문서는 아직 유효하지 않음 | | `"validated-pinned-flat-prefix"` | `false` | 요청 범위의 고정 본딩을 전용 경로로 검증·승인함. Flow 1.0 작성 검증 통과를 뜻하지 않음 | 부분 계산 성공을 전체 문서 수정·검증 완료로 보고하지 마세요. `validate()`로 남은 오류를 확인하고 전체 수정 후 다시 검증합니다. 이 검증 범위는 기존 백그라운드 캐시 정책을 바꾸거나 뒤 단계가 절대 계산되지 않는다고 보장하는 설정이 아닙니다. `computeTo`의 `timeoutMs` 기본값은 600,000 ms, 최대값은 3,600,000 ms입니다. 진행 콜백은 관측용이며 콜백 오류가 계산 결과를 바꾸지 않습니다. 타임아웃은 기다림이 종료됐다는 뜻으로, 엔진이 중지됐다는 보장이 아닙니다. `status()`를 다시 확인하고 실행 중이면 새 요청을 반복하지 마세요. `ArpelierError`의 `code`를 처리하세요. `NO_PROJECT`, `INVALID_FLOW`, `INVALID_STEP`, `BUSY`, `PROJECT_CHANGED`, `FLOW_CHANGED`, `COMPUTE_SUPERSEDED`, `NOT_COMPUTED`, `TIMEOUT` 등은 성공 결과가 아닙니다. 프로젝트·문서·선택 단계가 도중에 바뀌거나 계산이 취소·대체되면 이전 구조를 새 결과로 반환하지 않습니다. `validate`, `computeTo`, `status`의 `warningsScope`는 `available-measured-process-results`입니다. `warnings`는 현재 문서와 일치하는, 이미 확보된 공정 영향 관측만 요약합니다. `warningCoverage.checkedStepIds`와 `unavailableStepIds`를 함께 읽으세요. `observations`는 각 authored `stepId`와 실제 `executionStepId`를 연결합니다. `scope: "selected-iteration-per-authored-step"`이며 `complete`도 경고 지원 단계에서 선택된 반복 회차의 관측이 갖춰졌다는 뜻입니다. 모든 반복 회차·공정·물리 검증의 완료를 뜻하지 않습니다. `validate`와 `status`는 현재 레시피의 보유 관측을 확인하고, `computeTo`는 요청한 시점까지 실행한 접두부를 대상으로 합니다. 기본값은 추가 공정 영향 계산을 시작하지 않습니다. 빈 경고 배열을 전체 공정의 문제없음으로 해석하지 마세요. 무식각은 제거 부피 0, 무주입은 실제 적용 부피 0이며, 포화되어 같은 재료 ID가 남은 주입과 다릅니다. CMP 주의는 XY column별 제거된 고체 두께 합의 최댓값이 3000 nm 이상인 경우입니다. 이는 물리적 위험 기준이나 원인 진단이 아닙니다. 필요한 관측은 `computeTo`의 `inspectOutcomeSteps`로 명시할 수 있습니다. [Oxide window 예제](/docs/agents/examples/oxide-window.flow.json)의 `etch` 단계라면 다음처럼 요청합니다. ```js const result = await window.arpelier.computeTo("final", { inspectOutcomeSteps: ["etch"], }); console.log(result.warnings, result.warningCoverage); ``` 최대 64개의 서로 다른 활성 ETCH/WET ETCH/IMPLANT/CMP authored ID만 허용하며, 선택한 계산 대상 뒤의 단계와 확산 전용 IMPLANT는 지정할 수 없습니다. 잘못된 요청은 선택 변경·계산 시작 전에 `INVALID_INSPECTION`으로 거부합니다. 분석 경로가 없으면 `INSPECTION_UNAVAILABLE`입니다. 명시한 관측은 기존 격리 분석·캐시를 순서대로 사용하므로 추가 시간이 들 수 있으며, 계산과 관측이 하나의 `timeoutMs` 예산을 공유합니다. 오래되거나 누락된 관측을 0으로 바꾸지 않습니다. ## 상태와 메모리 `status()`의 `saveState`는 `empty`, `saving`, `saved`, `unsaved`이고 `saveScope`는 `none`, `account`, `local`입니다. `dirty`, `savedAt`, `projectId`를 함께 확인하세요. 이 상태의 `saved`는 계정 저장을 뜻합니다. 암호화 `.apl` 로컬 파일의 저장 완료는 파일 저장 UI에서 별도로 확인하고, 이를 계정 저장으로 해석하지 마세요. `computedUpTo`는 현재 입력·선택과 일치하는 완료 결과가 있을 때만 ID이며, 없거나 오래된 결과이면 null입니다. `progress`도 현재 실행의 관측입니다. `gridNm`은 실제 계산 간격, `cells`는 XY 열 개수입니다. Z 복셀을 곱한 조밀한 격자 개수가 아닙니다. `engineBufferBytes`와 `memoryMB`의 범위는 엔진 소유 버퍼와 캐시이며 `memoryScope`로 명시합니다. `heap`은 지원되는 경우 현재 JS 실행 문맥만의 힌트입니다. 진행 콜백의 `memoryMB`는 `memoryScope: "current-js-context"`인 heap 힌트이므로 `status().memoryMB`와 범위가 다릅니다. Worker·GPU·탭 전체 메모리로 해석하지 말고 알 수 없는 null을 0으로 바꾸지 마세요. ## 수치 단면 `axis`는 절단면에 수직인 축입니다. `y`는 XZ 단면, `x`는 YZ 단면, `z`는 XY 단면입니다. `at`은 카메라 좌표가 아닌 프로젝트 좌표의 nm이며, 해당 축 범위 내부에 있어야 합니다. 수직 단면의 `dz` 기본값은 엔진의 Z 양자화 간격이고 이보다 작게 지정할 수 없습니다. 반환값의 `materials[row][column]`은 수치 재료 ID입니다. `horizontalAxis`, `verticalAxis`, `spacing`, `origin`, `width`, `height`, `units`, `sampling`, `computedUpTo`를 함께 읽으세요. 행은 수직 축의 양의 방향으로 증가합니다. 원점은 0이고 셀 중심에서 표본을 읽습니다. 백만 표본을 초과하면 `SECTION_TOO_LARGE`로 거부합니다. 수직 단면에서는 dz를 늘려 크기를 줄일 수 있으며, XY 단면은 프로젝트 XY 격자에 의존합니다. ## 연결성과 접촉 면적 추가 기능은 `api.capabilities?.inspectStructure`와 메서드의 존재를 확인하세요. 이 확장은 API/Flow 버전 1.0을 유지합니다. `inspectStructure({ checks })`는 현재 계산된 정준 구조를 직접 검사하며 계산·저장·선택 변경·렌즈 전환을 시작하지 않습니다. 기존 BONDING 구조도 계산 완료 상태이면 읽을 수 있으나 Flow 1.0의 BONDING 작성 지원이 추가된 것은 아닙니다. 각 검사에는 고유한 `id`, `type`과 선택적 `rationale`를 넣습니다. - `connection`: `materialIds`, `anchors: [{ xNm, yNm, zNm, materialId }, ...]`, `connected: true|false`. 여섯 방향의 면 접촉을 통해 선택 재료 전체에서 연결을 찾습니다. `false`는 모든 anchor가 서로 다른 점유 component에 있어야 통과합니다. 부분 연결은 절연 통과가 아닙니다. - `interface`: `boundsNm: { x: [a,b], y: [a,b], z: [a,b] }`, 서로 다른 `materialA`, `materialB`, `minAreaNm2`, 선택적 `maxAreaNm2`. 영역에 잘린 실제 공유면의 `areaNm2`, `byAxisNm2`, `pass`를 반환합니다. 영역은 각 축의 하한 포함·상한 제외입니다. ID는 현재 문서에 등록된 숫자형 고체 재료의 **정확한 ID**입니다. 예를 들어 `[5]`가 3–7로 확장되지 않습니다. anchor는 기대한 재료 내부에 두세요. 잘못 놓인 anchor는 `status: "invalid-anchors"`, `connected: null`, `separate: null`, `pass: false`이며 단락이나 절연의 증거가 아닙니다. 접촉 면적·연결은 기하학적 관측으로 접촉 저항·전류·접합 동작의 시뮬레이션이 아닙니다. 요청당 검사 1–16개, 서로 다른 연결 재료 집합 최대 4개, 집합당 중복 없는 ID 1–16개, 연결 검사당 anchor 2–8개를 허용합니다. 좌표는 유한한 0–1,000,000 nm, XY는 현재 도메인 내부여야 하고 영역 폭은 양수여야 합니다. 면적 기준도 유한한 음이 아닌 수이며 상한은 하한 이상입니다. 미지원 유형·필드·알 수 없는 ID는 `INVALID_INSPECTION`입니다. 무거운 작업 배열 할당 전에 두 단계로 계수합니다. 먼저 열 길이만으로 전체 구간 훑기(구간 수 × 재료 집합 수)를 제한하고, 다음으로 각 재료 집합에 속한 구간만 세어 추가 작업 메모리와 예상 작업량을 제한합니다. 브라우저 앱은 훑기 1,200,000,000·메모리 512 MiB·작업량 800,000,000 단위·시간 120초, MCP 서버는 훑기 600,000,000·메모리 256 MiB·작업량 400,000,000 단위입니다. 현재 한도는 `capabilities.inspectStructure.limits`에서 확인하세요. 측정 예: 32단 적층 640,000열(2.5 nm)에서 금속 두 종류의 선택 구간 1,330만 개를 약 9초에 검사했습니다. 연결 그래프는 선택한 재료의 구간만 보관하므로, 높은 적층 구조도 검사할 재료를 좁히면(예: 배선 금속 하나) 전체 영역을 검사할 수 있습니다. 초과 시 `INSPECTION_TOO_LARGE`, 실제 작업 배열 할당 실패 시 `INSPECTION_ALLOCATION_FAILED`이며 완료된 구조와 해상도는 유지됩니다. 작은 interface 영역이나 필요한 검사로 요청을 좁히세요. 연결 검사는 경로가 영역 밖으로 이어질 수 있어 전체 계산 구조를 분석합니다. 자동 해상도 저하나 빈 성공 결과를 사용하지 않습니다. 응답의 `projectId`, `computedUpTo`, `revision`을 측정값과 함께 보관하세요. `revision`의 flow 표식과 프로젝트·런타임 세션, 엔진 generation은 현재 작업 문맥을 식별하며 영구 계정 리비전이나 암호학적 형상 해시가 아닙니다. 도중 프로젝트·문서·선택·엔진이 바뀌면 이전 결과를 반환하지 않습니다. `admission.memoryScope: "inspection-working-arrays-and-results"`의 예상 메모리는 원래 정준 구조·캐시·탭 전체 메모리를 포함하지 않습니다. 실행 예제는 [구조 검사](/docs/agents/tasks/inspection.md)를 참고하세요. MCP의 `inspect_arpelier_project`는 자체 문서·checkpoint 계약과 추가 검사 유형을 사용하므로 브라우저 메서드에 MCP 전체 인자를 넘기지 마세요. ## 본딩 전용 API `api.capabilities?.bonding`과 각 메서드의 존재를 확인하세요. Flow 1.0 작성용 `validate`·`setFlow`는 BONDING이나 외부 asset 입력을 계속 거부합니다. 새 본딩은 현재 편집 가능한 프로젝트의 **계산 완료된 최종 활성 단계 뒤에 추가**합니다. `resumePinned`/`updatePinned` capability가 있으면 지원 범위의 저장 본딩 재개·원본 갱신도 가능합니다. 모듈 그룹이 있는 문서도 됩니다(`capabilities.bonding.groupedFlows`). 반복 그룹(`cycleCount` 2 이상) 안에 든 본딩, 고급 배치, 다른 원본으로 교체, 공식 예제 원본 선택은 UI를 사용하세요. 반복 그룹이 본딩 앞에만 있으면 API로 됩니다. 입력은 [본딩 예제](/docs/agents/tasks/bonding.md#브라우저에서-준비검토적용계산)처럼 계정 프로젝트 또는 현재 문서의 공정 ID를 지정하며 원본 문서나 계산 배열을 직접 받지 않습니다. `prepareBonding({ sourceProjectId, afterStepId, stepId, name?, note?, group?, placement?, acceptSourceDisclosure? }, options?)`는 기존 계정 읽기 권한으로 원본을 조회하고 최종 결과의 준비 상태·리비전·fingerprint·실제 구조 digest를 확인합니다. 유효한 최종 캐시가 없으면 격리 Worker에서 원본을 준비합니다. 계정 캐시 업로드·최근 프로젝트 기록·원본 저장·공유 권한 변경은 하지 않습니다. `note`는 다른 공정과 같은 본딩 단계의 메모입니다(20,000자까지). 무엇을 왜 붙이는지, 치수 출처, 이 구조에서의 위험을 적으세요. `group: { id, name?, note? }`를 주면 본딩 단계 하나를 감싼 모듈 그룹을 함께 만듭니다. 그룹 끝 단계 ID는 `-end`입니다. `applyBonding`이 돌려주는 `lastStepId`를 다음 `appendFlow`의 `expectedLastStepId`로 쓰세요. 비공개·초대 범위 원본은 승인 인자가 없으면 구조 준비 전에 `{ prepared:false, requiresSourceDisclosure:true, source }`로 멈춥니다. 원본 구조 복사본이 대상에 포함되어 나중에 대상과 함께 공유될 수 있습니다. 사용자가 이 복사를 승인했을 때만 `acceptSourceDisclosure:true`로 다시 준비하고 적용하세요. AI가 읽기 권한만으로 복사·노출 동의를 대신 판단하면 안 됩니다. `placement`는 nm 단위 `offsetX`/`offsetY`, `flipSource`(기본 true), `flipAxis`(기본 x; x/y/xy), `basePlanarize`/`sourcePlanarize`(기본 false), `paddingMaterialId`를 지원합니다. 평탄화 시 대상에 등록된 padding 재료가 필요합니다. 오프셋은 출력 격자에 맞춰지므로 반환된 배치를 확인하세요. 이 초기 경로는 XY/Z 격자 세분화·캔버스 확장·배열 반복·이름만으로 재료 병합을 허용하지 않으며, 기존 해상도를 유지할 수 없는 조합은 거부합니다. 고급 배치에는 UI를 사용하세요. 성공 응답은 `{ prepared:true, token, expiresAt, source, placement, materialChanges, ... }`입니다. 실제 접촉, 최종 출력 격자와 재료 매핑을 검토한 뒤 `applyBonding`에 토큰을 명시합니다. 토큰은 하나만 보유하며 5분 후 만료되고 새 준비·취소·프로젝트 문맥 변경 때 해제됩니다. 적용 시 원본 접근·리비전·최종 구조와 대상 정준 구조를 다시 확인합니다. 토큰은 적용 시도 한 번으로 소비됩니다. `BONDING_SOURCE_CHANGED`, `BONDING_CONTEXT_CHANGED`, `BONDING_PREPARATION_EXPIRED` 등으로 실패하면 변경하지 않고 새 검토를 요청합니다. 입력·출력은 각각 최대 262,144열이며 원본 작성·재생 단계는 각각 최대 1,024개입니다. 해상도와 재료·Origin·경계 값을 유지한 compact 입력 복사는 각 16 MiB, 기존 저장 구조 복원과 격리 계산은 각 64 MiB, 검토 Worker의 실제 출력 작업 배열은 64 MiB로 제한합니다. 검토 결과는 접촉·배치 요약만 반환하며 전체 출력 구조를 메인 화면으로 복사하지 않습니다. 격리 재계산의 v2 본딩도 실제 출력 크기를 계수하여 작업 배열 할당 전에 64 MiB 한도를 검사합니다. 준비된 초안은 문자열·재료·작업 배열을 포함한 보수적 payload 추정 64 MiB와 asset 문자열 8,388,608자 한도로 제한합니다. 메타데이터를 먼저 검사하고 복원 전 한도와 복원 후 실제 compact 복사 한도를 각각 확인합니다. `BONDING_AUTOMATION_LIMIT`/`BONDING_SOURCE_BUDGET_EXCEEDED`는 해상도 저하로 우회하지 마세요. 이는 작업 payload·decode 한도이며 전체 탭 heap, 모든 일시 복사본, 사용 가능한 RAM이나 네트워크 수신량의 보장이 아닙니다. 기존 Storage SDK는 Blob 다운로드를 먼저 끝내므로 이미 시작된 파일 읽기는 취소 뒤에도 완료될 수 있지만, 취소된 파일을 decode하거나 적용하지 않습니다. 준비·재개 검증·적용의 선택적 `options`는 `{ signal, timeoutMs, onProgress }`입니다. `AbortSignal` 취소는 즉시 `CANCELLED`로 대기를 끝내고 격리 준비 Worker를 종료합니다. 입력 복사 할당 실패는 `BONDING_AUTOMATION_COPY_FAILED`이며 기존 완료 엔진을 폐기하지 않습니다. 계산은 적용 또는 재개 검증 뒤 `computeBonding(stepId, { timeoutMs, onProgress }?)`로 별도 실행하고, `{ computed:true, computedUpTo }`를 확인하세요. 계산 timeout은 기존 `computeTo`와 같이 계산 중지를 보장하지 않으므로 `status()`를 확인하세요. 본딩 전용 메서드의 timeout은 최대 600,000 ms, 기본 120,000 ms이며 계정 저장은 별도 `save()` 성공으로 확인합니다. `computeBonding`은 지정한 본딩 단계까지만 계산합니다. `capabilities.bonding.computeTo === true`이고 `computeScope: "validated-pinned-flat-prefix"`이면 현재 문서의 본딩 뒤 일반 공정도 `computeTo(stepId)` 또는 `computeTo("final")`로 계산할 수 있습니다. 요청한 범위 안의 모든 활성 BONDING이 `applyBonding` 또는 `resumeBonding`으로 명시적으로 승인되어 있어야 하며 자산·재료·배치와 편집 문맥을 다시 확인합니다. 자동 재개·원본 조회는 하지 않습니다. 본딩 뒤 공정은 `appendFlow`로 이어 붙입니다(`capabilities.bonding.appendFlowAfter`). 본딩이 원본에서 가져온 재료는 `getFlow().materials`에 있으니 그 ID를 쓰세요. 이어 붙이기는 본딩을 포함한 기존 단계를 바꾸지 않습니다. 그래서 이미 승인된 본딩은 승인이 유지되고, 다시 `resumeBonding`할 필요 없이 `computeTo`로 계산합니다. `setFlow`는 본딩이 있는 문서를 바꾸지 못합니다. `getFlow()`는 본딩 단계의 고정 자산을 `bondingAsset: { omitted:true, digest, characters }`로만 보여 줍니다(자산 본문은 수백 kB일 수 있음). 반환된 `computed`, 실제 목표 단계의 `computedUpTo`와 해상도를 확인하고 필요한 구조 검사·계정 저장은 별도로 완료합니다. 저장·재열기나 새 API 인스턴스에서는 `resumeBonding({ stepId })`로 다시 검증합니다. 대상 편집 권한, 저장 codec의 자산 무결성과 실제 digest, 원본 식별, 재료 정의·매핑·의미 및 배치 승인을 해당 단계까지의 격리 prefix 계산으로 확인하며 앞선 활성 본딩도 함께 검증합니다. 실행 범위의 모든 활성 본딩이 반환된 `authorizedStepIds`에 포함되는지 확인하고 부족한 범위만 추가로 검증하세요. 저장된 승인 flag는 사용하지 않습니다. `{ authorized:true, computed:false }`는 활성 화면의 계산·저장 완료가 아니며, 검증 자체에는 추가 계산 시간이 듭니다. 계정이나 열린 프로젝트의 작업 세션, 검증한 공정 접두부·마스크·재료·자산이 바뀌면 계산 승인이 만료되어 다시 검증해야 합니다. 저장본의 세분화·확장 허용 flag가 true여도 실제 배치가 격자나 영역 변경을 요구하지 않으면 검증할 수 있습니다. 양면 평탄화가 꺼진 상태의 효과 없는 spin 채움도 저장값·기존 배치서명을 바꾸지 않고 읽습니다. 실제 격자 변경이나 효과 있는 고급 채움이 필요한 경우에는 UI를 사용하세요. 고정본은 이미 대상에 포함된 자료이므로 원본이 삭제·변경되거나 접근 불가여도 유효한 고정본의 재개 계산을 차단하지 않고 private 공개 범위 동의를 다시 요구하지 않습니다. 기본값은 원본 조회를 하지 않습니다. 선택적 `compareSource:true`는 metadata 비교만 하며 `sources[].sourceStatus`가 `same-revision`, `changed`, `unavailable` 또는 기본 `not-checked`입니다. `sources[].comparisonScope: "metadata-revision"`, `contentCompared:false`는 내용 비교를 하지 않았음을 명시합니다. `changed`도 레시피·형상이 바뀌었다는 확정이 아닙니다. 같은 리비전이라는 관측도 원본 전체 형상 동일성의 증명이 아니며, 비교 실패나 원본 변경이 고정본 자동 교체를 뜻하지 않습니다. 실제로 최신 원본을 복사하려면 `prepareBondingUpdate({ stepId, placement?, acceptSourceDisclosure? })`를 사용하세요. 현재 원본 접근·private 동의·최종 구조 준비를 다시 확인하고 `{ replacing:true, previousSource, source, token, ... }`을 반환합니다. 검토 후 같은 `applyBonding`으로 적용하면 공정 ID·이름·메모를 유지하며 고정 자산·재료 매핑을 Undo 한 단계로 교체합니다. 준비만 하거나 취소·stale·실패한 경우 기존 고정본은 유지됩니다. 갱신 적용 후 계산과 계정 저장은 별도로 완료해야 합니다. ## 계정 저장 `save()`는 로그인하지 않았으면 `LOGIN_REQUIRED`, 공식 예제·읽기 전용·별도 저장 UI가 필요한 경우에는 `SAVE_REQUIRES_UI`로 중단합니다. 해당 UI 절차를 사용한 후 상태를 다시 확인하세요. 정보 창 확인, 계산 완료, 로컬 복구 성공은 계정 저장 성공이 아닙니다. `{ saved: true }`를 확인하기 전에는 저장했다고 보고하지 마세요. 계정 문서 저장 성공이 선택 단계의 계산 캐시나 모든 중간 단계의 보존을 보장하지는 않습니다. 계산 결과를 다시 열어 사용하는 것이 요청에 포함됐다면 다음 절차로 별도 확인하세요. ## AI 생성 기록 저장한 적 없는 새 프로젝트에 `setFlow`로 문서를 넣거나, 빈 새 프로젝트에 `appendFlow`로 공정을 쌓으면 앱이 그 프로젝트를 AI가 만든 것으로 기억합니다. 처음 계정에 저장할 때 프로젝트 문서에 기록이 남고(만든 방식, AI 이름, 처음 저장 시각, 그때 공정 수), 대시보드 카드와 편집기 상단에 "AI 생성" 표시가, 프로젝트 정보에 생성 출처와 "AI가 만든 프로젝트입니다. 실수가 있을 수 있고, 실제 디바이스와 다를 수 있습니다." 안내가 보입니다. 소유자는 공유 설정에서 카드·상단 표시를 끌 수 있고, 프로젝트 정보의 기록과 안내는 그대로 남습니다. - 두 메서드의 두 번째 인자에 `{ agent: "Claude" }`처럼 사용자가 알아볼 제품 이름을 주세요(40자까지, 문자열이 아니면 `INVALID_OPTIONS`). 앱은 이 이름을 확인하지 않고 "에이전트가 밝힌 이름"으로 보여 줍니다. 처음 준 이름이 유지됩니다. - 사람이 만든 공정 뒤에 `appendFlow`로 덧붙인 경우, 이미 저장된 프로젝트를 바꾼 경우, 복제(Save as)한 사본은 기록하지 않습니다. - `status().aiProvenance`가 `{ state: "pending" | "recorded" | "generated", method, agent, hidden }` 또는 `null`입니다. 저장 뒤 `state: "recorded"`면 기록된 것입니다(`api.capabilities?.provenance`). ## 저장본을 다시 열 때 1. UI 또는 현재 제공된 열기 도구로 의도한 계정 프로젝트를 엽니다. 프로젝트 로딩·캐시 복원·첫 화면 생성을 기다리고, 진행 중 `computeTo`나 변경 요청을 겹쳐 보내지 않습니다. 2. 열기가 끝난 뒤 `status()`에서 `projectId`, 실제 `gridNm`, `computedUpTo`를 의도한 프로젝트·해상도·선택 단계와 비교합니다. 복원된 결과가 이미 목표 단계와 일치하면 활용하고, 완료 결과가 없거나 다른 단계를 원할 때만 지원 경로로 계산합니다. `computedUpTo`는 현재 결과의 완료 여부이며 캐시에서 복원됐는지 새로 계산됐는지까지 알려주지는 않습니다. 3. 계정 저장 확인은 `saveState: "saved"`, `saveScope: "account"`, `dirty`, `savedAt`을 함께 읽습니다. 형상 보존까지 확인하려면 같은 리비전·단계의 필요한 단면·재료·연결 관측을 저장 전과 비교하세요. `save()` 응답이나 마지막 화면만으로 이 검증을 대신하지 않습니다. 재열기 성공은 확인한 선택 단계와 검사 범위에 한해 보고합니다. 캐시 복원과 재계산을 확인할 수 있으면 구분하고, 출처를 관측할 수 없으면 미확인으로 남깁니다. 검증 과정에서 사용자 공정·해상도·본딩 원본을 바꾸지 마세요. 대형 작업의 중복 실행과 메모리 오류 대응은 [격자·계산 복구 안내](/docs/agents/performance.md#메모리-실패와-재시도)를 따릅니다. ## 연결된 도구 일반 브라우저에서는 앱이 제공하는 편집·계산·저장 UI를 사용합니다. 연결된 MCP 환경에서는 서버가 실제로 제공하는 도구 목록과 각 도구의 스키마를 읽으세요. 배포 환경에 따라 제공 여부가 다릅니다(현재 운영 `https://arpelier.com/mcp`와 프리뷰·개발 환경). Arpelier MCP 서버(`/mcp`)는 Flow 1.0 문서를 받아 처리하는 서버입니다. 계정·세션이 없고 누구의 프로젝트에도 쓰지 않습니다. 도구 11개 중 10개는 읽기 전용이고, 긴 계산을 이어 가기 위한 계산 상태만 최대 1시간 남깁니다([서버 계산의 범위](/docs/agents/performance.md#서버-계산의-범위)). `arpelier_open_in_app`은 Flow를 저장 전 새 프로젝트로 여는 링크를 만들고(넣을 수 있으면 마지막 공정의 계산 결과 포함), 그 링크를 7일 동안 보관합니다([MCP 결과와 계정 프로젝트 구분](#mcp-결과와-계정-프로젝트-구분)). 그림은 두 가지입니다. `arpelier_cross_section`에 `image: true`를 주면 단면을 재료 색으로 칠한 PNG(최대 1024 px, 실제 비율, nm/px 포함)를 함께 돌려주고, `arpelier_render_structure`는 도메인의 한 모서리에서 본 3D 구조 PNG를 돌려줍니다(`hiddenMaterialIds`로 감싸는 재료를 빼고 안쪽을 볼 수 있음, 한 변 256열이 넘는 격자는 표본). 그림은 구조를 보는 용도이고 수치는 단면 `stacks`·`inspectStructure`에서 읽습니다. `arpelier_measure_section`은 수직 단면에서 재료나 빈 공간(0: 표면 위 개구부, 1: 표면 아래 void)의 높이별 폭(CD), 측벽 각도(90°가 수직), 막(`filmMaterialId`)의 윗면·바닥·측벽 두께와 단차 피복비(step coverage)를 잽니다. 가로 값의 해상도는 격자 간격이고, 한 열보다 얇은 막은 벽에 나타나지 않습니다. `arpelier_sweep_flow`는 공정 필드 최대 2개(`thickness`, `depth`, `materialetch.18`처럼 그 공정에 이미 있는 필드)를 주어진 값들로 바꾼 변형(조합 최대 16개)을 각각 계산해, 변형마다 표면 높이·재료 부피·void와 `measure`(측정 도구와 같은 입력)를 준 경우 그 측정값을 한 줄씩 돌려줍니다. 앞부분이 같은 변형은 보관된 계산 상태를 함께 쓰고, 한 번의 호출 시간에 못 끝낸 줄은 `pending`이며 같은 호출을 다시 하면 이어서 계산합니다. `arpelier_open_in_app`의 `view`는 `setView`와 같은 형식(`highlightMaterialIds` 제외)으로 링크를 연 프로젝트의 보기(투명·숨긴 재료, 단면, 카메라, 표면, 조명, 바닥 그리드 등)를 저장된 보기 설정으로 넣습니다. 카메라와 z 단면은 링크에 계산 결과가 들어갈 때만 적용됩니다(아니면 `view.skipped`). `export`(`glb`·`3mf`·`stl-zip`)를 주면 링크를 데스크톱 브라우저에서 열 때 앱이 3D 내보내기 메뉴를 그 형식으로 골라 열고, 연 사람이 내보내기를 눌러 파일을 저장합니다. 파일은 브라우저에서 만들어지고 서버에는 만들거나 남기지 않습니다. `arpelier_preview_structure`는 MCP Apps를 표시하는 클라이언트(Claude)에서 대화 안에 상호작용 3D 미리보기를 띄웁니다. 공정 타임라인에서 단계를 고르고 회전·확대·전체 화면·Arpelier에서 열기를 할 수 있으며, 계산은 사용자의 브라우저에서 앱과 같은 엔진으로 합니다. XY 열 4만, 실행 단계 150, 열×단계 300만, 데이터 약 120 kB를 넘는 구조는 서버가 그린 그림으로 대신 보여 줍니다(`interactive: false`). 또 이를 지원하는 클라이언트(MCP Apps)에서는 `arpelier_render_structure`와 `arpelier_cross_section`의 그림이 대화 안에 재료 색 범례와 함께 표시됩니다. `arpelier_read_docs`·`arpelier_validate_flow`·`arpelier_compute_flow`·`arpelier_cross_section`·`arpelier_inspect_structure`가 이 문서의 `validate`·`computeTo`·`getCrossSection`·`inspectStructure`에 대응하며(`arpelier_export_structure`는 아래 [3D 파일로 내보내기](#3d-파일로-내보내기)의 재료 정보만 돌려줌), 같은 Flow 검증기와 GridEngine을 사용합니다. `computeTo`처럼 `stepId`에 작성 공정 ID(Group은 GROUPSTART ID로 완료 시점)를 지정합니다. 단면 도구의 `stacks`는 세로 단면의 열 유형별 층 경계(fromNm·toNm·thicknessNm)와 그 유형이 나타나는 가로 구간(`rangesNm`)이며, 엔진 값에서 직접 읽으므로 `dz`와 무관합니다. 세로 단면의 문자 지도도 같은 경계로 그려 모든 층이 나타나지만, 한 글자보다 얇은 층은 한 줄로 확대되므로 두께는 `stacks`로 확인하세요. ## 보기 설정 (보여주기) `setView`는 계산이 끝난 구조를 보기 좋게 보여주는 기능입니다. 공정 문서와 계산 결과는 바꾸지 않습니다. 준 항목만 바뀌고 나머지는 그대로 둡니다. - `transparentMaterialIds`: 투명하게 할 재료 ID 목록. 주어진 목록으로 교체됩니다(빈 배열이면 모두 불투명). - `transparentOpacity`: 투명 재료의 불투명도 0–1. `0`이면 그 재료를 숨깁니다. 기본 0.08. 재료마다 다르게 하려면 `setAppearance`의 `transparentOpacity`를 쓰세요(둘은 곱해져 적용됩니다). - `highlightMaterialIds`: 강조할 재료 ID 목록. - `cutaway`: 3D 단면입니다. `{ x?: [min, max], y?: [min, max], z?: [min, max] }`(nm, 프로젝트 좌표)는 **잘라내서 없앨 상자**입니다. 적지 않은 축은 전체 범위입니다. 예: `{ y: [0, 40] }`은 y 0–40 nm 쪽 절반을 잘라내 단면을 보여주고, `{ x: [100, 200], y: [0, 40], z: [200, 760] }`은 한 모서리를 잘라냅니다. `null`이면 단면을 끕니다. 남길 구간 하나만 고르는(위아래를 동시에 자르는) 기능은 아닙니다. - `camera`: `{ preset?, azimuthDeg?, elevationDeg?, zoom?, target?, fovDeg? }`. `preset`은 `iso`(방위각 −60°, 고도각 28°)·`front`·`back`·`left`·`right`·`top`. 방위각은 +x에서 +y 쪽으로 잰 카메라 방향(도), 고도각은 바닥면 위로 잰 각도입니다. `zoom: 1`은 구조 전체가 화면에 들어오는 거리이고, 클수록 가까워집니다. `target: { xNm, yNm, zNm }`로 확대할 지점을 정할 수 있습니다. `fovDeg`(15–75, 기본 45)는 시야각이며 작을수록 원근 왜곡이 줄어듭니다. 카메라를 주면 매번 그 각도로 다시 맞추므로 원하는 각도를 함께 주세요. - `surface`: `"voxel"`(계산 격자 그대로, 기본) 또는 `"smooth"`(매끄럽게). smooth는 켤 때 표면을 새로 만들어 큰 격자에서는 수십 초가 걸릴 수 있고, **단면(`cutaway`)을 바꿀 때마다 표면 전체를 다시 만듭니다.** 불투명한 재료끼리 맞닿은 면은 그리지 않으므로 그리는 양은 voxel과 비슷하지만, 만드는 시간과 메모리는 voxel보다 훨씬 큽니다(예: 32만 열 3D NAND에서 표면 삼각형 voxel 33만 개, smooth 720만 개. 단면 한 번에 voxel 약 0.3초, smooth + 툰 약 3.5–4.5초, 10코어 M4 기준. 큰 표면은 여러 워커가 나눠 만들므로 코어가 적은 컴퓨터에서는 더 걸림). 반투명 재료는 모든 면을 그려 더 무거워집니다. - `lighting`: `{ style?, intensity?, mainLight?, toonShade?, shadow?, direction? }` - `style`: `"standard"`(재질 질감과 광원 음영, 그림자. 기본) 또는 `"toon"`(면 단위 카툰 음영과 자동 윤곽선. 일러스트처럼 보임). 큰 표면의 윤곽선은 표면이 나온 뒤 따로 계산해 붙습니다(`render.featureLinesMs`). `setView`와 `captureView`는 윤곽선이 다 그려질 때까지 기다립니다. 둘은 서로 다른 화풍이고 표면(`voxel`·`smooth`)과 자유롭게 조합됩니다. `toonShade`는 카툰 음영의 세기입니다. `dark`는 윗면과 옆면의 차가 커서 입체가 잘 보이고, `normal`은 부드럽고, `bright`는 면이 바래 보이며, `none`은 음영 없이 재료 색과 윤곽선만 남깁니다. 카툰은 색을 밝고 흐리게 만들므로 한 단계 진한 색이 어울립니다. - `intensity`: 주 조명 밝기 0–2(기본 1). `mainLight: false`이면 주 조명을 끕니다(방향과 그림자도 꺼짐). - `shadow`: `true`/`false` 또는 `{ enabled?, softness?, reach? }`(각 0–1). 바닥에 드리우는 그림자입니다. - `direction`: `{ azimuthDeg, elevationDeg }` 빛이 오는 방향. 방위각은 카메라와 같은 기준이고 고도각은 22–84°입니다. 그림자 방향도 함께 바뀝니다. - `overlays`: `{ floorGrid?, axes?, ruler? }` 바닥 그리드, 좌표축, 눈금자(축척 막대)를 켜거나 끕니다. `getView()`의 `render`(읽기 전용, 버전 1.2부터)는 지금 화면의 그리기 비용입니다. smooth를 켠 뒤 확인하세요. - `triangles.surface`: 지금 보이는 재료 표면의 삼각형 수. `triangles.drawn`: 그중 실제로 그리는 수(가려진 경계면을 뺀 값). 반투명 재료가 많을수록 `drawn`이 커집니다. - `geometryMB`: 재료 표면 형상이 차지하는 메모리(MB). - `smoothBuildMs`: 마지막 smooth 표면이 화면에 나오기까지 걸린 시간(ms, 사용자의 기기 기준). `smoothFromCache`는 저장된 캐시에서 읽었는지입니다. 이번 세션에서 smooth를 만든 적이 없으면 둘 다 `null`입니다. - `featureLinesMs`(버전 1.4부터): 툰일 때 마지막으로 윤곽선을 만드는 데 걸린 시간(ms). 표면을 다시 만든 뒤, 툰으로 바꾼 뒤, 숨긴 재료를 다시 보이게 한 뒤에 붙는 윤곽선이 모두 해당합니다. 툰이 아니거나 아직 없으면 `null`입니다. - **단면을 한 번 바꾸는 비용 ≈ `smoothBuildMs` + `featureLinesMs`**입니다. 사용자가 단면을 움직일 때마다, 그리고 캐시 없이 프로젝트를 열 때마다 이만큼 기다립니다. 불투명도 0으로 숨긴 재료는 `triangles`에 들어가지 않지만 표면은 만들어지므로 이 시간에 포함됩니다. - 기준: - `triangles.drawn`이 300만을 넘으면 반투명 층을 숨김(0)으로 바꿉니다. - 단면 비용이 3초 이하면 그대로 둡니다. - 3–8초면 smooth로 저장해도 되지만, 단면을 바꿀 때마다 그만큼 걸린다고 사용자에게 한 줄로 알립니다. 화면에서 단면 손잡이를 끄는 동안은 voxel 미리보기가 바로 따라오고, 놓으면 그 단면이 voxel로 먼저 나온 뒤 smooth로 바뀝니다(smooth 생성이 1.5초 이상인 구조). `setView({ cutaway })`는 smooth가 다 나온 뒤에 돌아옵니다. - 8초를 넘거나 `geometryMB`가 300을 넘으면 저장할 보기는 voxel로 두고(깔끔한 도식이면 voxel + toon) smooth는 캡처할 때만 씁니다([완성 후 보여주기](/docs/agents/tasks/real-structures.md#8-완성-후-보여주기)). ```js const api = window.arpelier; await api.setView({ transparentMaterialIds: [18, 19], transparentOpacity: 0, cutaway: { y: [0, 40] }, surface: "smooth", lighting: { intensity: 1.2, shadow: { enabled: true, softness: 0.7 }, direction: { azimuthDeg: -25, elevationDeg: 55 } }, overlays: { floorGrid: false, axes: false }, camera: { preset: "iso", elevationDeg: 38 } }); await api.setView({ camera: { azimuthDeg: -80, elevationDeg: 12, zoom: 3, target: { xNm: 100, yNm: 40, zNm: 400 } } }); ``` ### 재료 겉모습 `setAppearance`는 재료 패널의 겉모습 설정을 바꿉니다. 모든 항목을 먼저 검사하고, 하나라도 틀리면 아무것도 바꾸지 않고 그 위치(`path`)를 알려줍니다(`INVALID_APPEARANCE`). - `color`: `"#rrggbb"`, `"#rgb"` 또는 `"rgb(r, g, b)"`. - `metalness`, `roughness`: 0–1. 주면 그 재료는 사용자 지정 값으로 고정됩니다(주지 않으면 프리셋의 자동 값). - `surfacePreset`: 재료 종류마다 고를 수 있는 표면입니다. 금속 `clean`(기본)·`film`(브러시드 막)·`microstructure`(다결정 금속, 결정립과 강한 금속 반사. 넓은 면이 각도에 따라 검게 보일 수 있음), 반도체 `clean`·`film`·`microstructure`·`emissive`, 절연체 `clean`·`film`·`microstructure`·`concrete`·`basalt`·`stained-glass`. 고를 수 있는 목록은 `getAppearance()`의 `surfacePresets`에 있습니다. - `transparentOpacity`: 이 재료가 투명할 때의 불투명도 0–0.5(기본 0.08). ```js const { materials } = await api.getAppearance(); // volumeFraction > 0 인 재료가 구조에 있는 재료 await api.setAppearance({ materials: [ { id: 21, color: "#d9824e", surfacePreset: "film" }, // Cu { id: 22, color: "#c2b8a3", surfacePreset: "film" }, // TiN 라이너 { id: 20, color: "#7f8b99", surfacePreset: "film" }, // W ] }); ``` 겉모습을 바꾼 재료는 `getFlow()`의 재료 항목도 바뀝니다. 그 뒤에 `appendFlow`를 쓴다면 그 재료를 `materials`에서 빼거나 `getFlow()`의 현재 항목을 넣으세요. 그래서 겉모습은 구조를 다 만든 뒤 마지막에 정하는 것이 좋습니다. 겉모습 변경은 이미 만든 표면(smooth 포함)의 색과 질감만 바꾸고 형상을 다시 만들지 않으므로, smooth를 켠 상태에서 색을 여러 번 바꿔 봐도 됩니다. 어떤 장면과 겉모습으로 보여줄지는 [실제 소자 구조를 재현할 때 — 완성 후 보여주기](/docs/agents/tasks/real-structures.md#8-완성-후-보여주기)를 참고하세요. ## 3D 파일로 내보내기 `exportStructure`는 사용자가 보기 화면의 내보내기 메뉴에서 쓰는 것과 같은 코드로 3D 구조 파일을 만듭니다. 편집 권한이 있는 프로젝트의 데스크톱 화면에서만 됩니다(공식 예제, 모바일은 `ACCESS_DENIED`). 진행 중에는 메뉴에 진행 상태와 취소 버튼이 보이고, 사용자가 취소하면 `CANCELLED`입니다. - `format`: `"glb"`(Blender·웹, 기본) · `"3mf"`(CAD·3D 프린터, 재료마다 닫힌 솔리드, mm) · `"stl-zip"`(재료마다 STL 하나, ZIP). - `scaleMode`: `"nm-as-mm"`(1 nm = 1 mm, 기본) · `"um-as-mm"`(1 µm = 1 mm, 100 µm가 넘는 구조) · `"fit-1m"`(가장 긴 변 1 m. 여러 단계를 묶으면 가로 영역의 긴 변 기준으로 모든 단계가 같은 배율). - `includeHidden`: 보기에서 숨긴(불투명도 0) 재료도 넣을지. 기본 `false`. - `steps`: `"current"`(지금 보이는 단계, 기본) · `"groups"`(최상위 그룹마다 마지막 단계) · 단계 ID 배열(1–12개, 공정 순서로 정렬). 여러 단계는 단계마다 계산해(이미 계산한 단계는 저장된 결과 사용) 한 파일로 묶고, 끝나면 사용자가 보던 단계로 돌아갑니다. 그동안 3D 보기가 단계마다 바뀌며 공정을 고치면 취소됩니다. GLB는 한 파일 안에 단계마다 노드 하나를 옆으로(X 방향) 나란히 놓고, 3MF·STL은 ZIP 안에 단계별 파일·폴더로 둡니다. - `materialInfo`: 재료 정보(기본 `true`). GLB는 루트의 `extras.materials`와 각 메시의 `extras.material`, 3MF는 파일 안 `Metadata/arpelier-materials.json`, ZIP은 `materials.json`에 들어갑니다. - `deliver`: `"download"`(사용자의 내려받기 폴더로 저장, 기본) · `"summary"`(파일을 만들어 크기만 재고 저장하지 않음) · `"blob"`(같은 페이지 스크립트에 `Blob`을 돌려줌. 에이전트 도구로 큰 파일을 옮기기는 어려우니 보통 `download`를 씁니다). 결과: `{ delivered, format, scaleMode, files: [{ name, mime, bytes }], blob?, steps: [{ id, label, name? }], materials, stats: { materialCount, durationMs } }`. `materials`는 한 단계면 재료 항목 배열, 여러 단계면 단계별 `{ step, materials }` 배열입니다. 재료 정보(`schema: "arpelier.export-materials/1.0"`)의 재료 항목은 `id`, `name`, `type`, `base`·`doping`(기본 도핑 재료군에서 읽은 n/p와 +, 예: Si (n+) → `{ type: "n", level: "+" }`), `color`, `object`(파일 속 물체 이름), `hiddenInViewer`, `volumeNm3`(격자 칸 부피, smooth여도 격자 기준), `zRangeNm`(높이 범위), `boundsNm`(구조 좌표의 상자), `fromSteps`(재료 설정이 이 재료인 공정 ID, 기판 공정은 실제로 까는 재료 쪽: 도핑을 고르면 도핑된 재료, 재료를 비우면 기본 기판 재료)입니다. 3D에서는 두께가 위치마다 달라 한 숫자로 주지 않으니, 층 두께는 `getCrossSection`으로 재세요. 도핑 농도·전기적 특성·공정 조건은 앱이 계산하지 않으므로 없습니다. 오류 코드: `ACCESS_DENIED`, `NOT_COMPUTED`(지금 단계를 먼저 계산), `BUSY`, `INVALID_EXPORT`(옵션 값, `path`로 위치), `INVALID_STEPS`(그룹 없음·단계 ID 없음·12개 초과, `details.reason`), `ALL_HIDDEN`, `SIZE_LIMIT`(`details`에 단계·크기·한도. voxel 표면, 배열 수 줄이기, 단계 줄이기), `SOLID_FAILED`(3MF·STL로 닫지 못함. GLB로), `COMPUTE_FAILED`, `TIMEOUT`, `CANCELLED`. `APP_UPDATED`: 이 페이지를 연 뒤 Arpelier가 새로 배포되어 내보내기 코드를 불러올 수 없습니다(`details.pageBuildId`, `details.serverBuildId`). 필요하면 저장하고 페이지를 새로 고친 뒤 같은 호출을 다시 하세요. 단면을 여러 장 덤프하는 식으로 우회하지 마세요. `MODULE_UNAVAILABLE`도 같은 조치(새로 고침 후 재시도)입니다. 다른 API 호출도 같은 이유로 실패하면 같은 두 코드를 받습니다. ```js const api = window.arpelier; if (!api.capabilities?.export) throw new Error("exportStructure is unavailable"); const saved = await api.exportStructure({ format: "3mf", scaleMode: "nm-as-mm", steps: "groups" }); // saved.files[0].name이 사용자의 내려받기 폴더에 저장 요청된 파일입니다. ``` MCP 서버의 `arpelier_export_structure`는 Flow를 계산해 같은 형식의 재료 정보만 돌려주고 파일은 만들지 않습니다(호스팅 서버의 응답 크기·계산 시간 한계). 파일은 사용자의 브라우저에서 `exportStructure`로 만드세요. 내보낸 단계 하나짜리 3MF·STL(ZIP)은 사람이 웨이퍼 본딩 구조 선택창의 "파일" 탭에서 다시 본딩 원본으로 쓸 수 있습니다(원래 위치·크기·재료 ID 복원). 이 불러오기는 화면 전용이며 API에는 없으므로, AI는 파일을 고르거나 불러오기를 시도하지 말고 사람에게 안내만 하세요. ## MCP 결과와 계정 프로젝트 구분 MCP 서버에는 `setFlow`·`save`가 없고 계정·세션이 없으며 누구의 프로젝트에도 쓰지 않습니다. 도구가 검증·계산한 Flow는 사용자 프로젝트에 저장되지 않습니다(서버에는 이어 계산할 계산 상태만 최대 1시간 남습니다). 웹 앱에는 Flow JSON 파일을 여는 기능이 없습니다. 사용자가 결과를 아펠리에에서 보거나 자기 프로젝트로 남기기를 원하면, 검증을 통과한 최종 Flow로 `arpelier_open_in_app`을 호출해 돌려받은 `url`을 링크로 전달합니다. 링크를 열면 모든 공정이 채워진 새 프로젝트가 저장 전 상태로 열립니다. 링크에는 넣을 수 있을 때 마지막 공정의 계산 결과가 함께 들어가 앱이 다시 계산하지 않고 결과를 보여 줍니다. 최근 1시간 안에 이 서버에서 계산한 Flow는 보관된 계산 상태에서 이어 받아 1초 안팎에 끝나고, 그렇지 않으면 링크를 만들 때 약 1분까지 계산합니다. 응답의 `computedStructure`가 포함 여부와, 빠졌다면 이유(`time`·`too-large`·`hosted-limit`·`daily-limit`·`not-computable`)를 알려 주며, 빠진 경우에도 링크는 같은 프로젝트를 열고 앱이 그 자리에서 계산합니다. `time`이면 `arpelier_compute_flow`로 전체 Flow를 계산한 뒤(길면 여러 번 호출) 새 링크를 만들면 결과가 들어갑니다. 연 사람이 로그인해 저장해야 계정에 남습니다. 링크와 그 안의 프로젝트(이름·메모 포함)와 계산 결과는 7일 동안 보관되고 링크를 가진 누구나 열 수 있으므로 이 두 가지를 함께 알립니다. Flow를 고치면 새 링크를 만듭니다. 처음 저장하면 앱이 만든 방식(`AI가 Arpelier 커넥터로 만든 뒤 링크로 열어 옴`)과 `agent`로 준 이름을 생성 기록에 남깁니다. 아펠리에 프로젝트 탭을 함께 다룰 수 있고 사용자가 그 탭에서 만들기를 원하면 아래 [브라우저에서 모듈별로 만들기](#브라우저에서-모듈별로-만들기)로 넣습니다. 링크 도구가 오류(`OPEN_IN_APP_*`)를 돌려주면 결과 Flow JSON을 사용자에게 전달하고 미저장 상태임을 밝힙니다. ## 브라우저에서 모듈별로 만들기 설계와 수치 확인은 MCP 도구로 먼저 끝냅니다. 그다음 열린 앱에서 모듈 단위로 공정을 넣고 계산해, 사용자가 공정 목록과 구조가 쌓이는 과정을 화면에서 보게 합니다. 모듈은 기능 묶음입니다(예: 하드마스크 → 패터닝·식각 → 라이너·갭필·CMP → 마스크 제거). 각 모듈은 `GROUPSTART`·`GROUPEND`로 묶어 이름과 메모를 달고, 공정마다 `note`를 채웁니다([공정 메모](/docs/agents/tasks/real-structures.md#3-모듈로-묶고-근거를-기록하기)). 1. `window.arpelier`와 `api.capabilities?.appendFlow`를 확인합니다. 열린 프로젝트에 이미 공정이 있으면 교체 전에 사용자에게 묻습니다. 빈 프로젝트나 새 프로젝트에만 바로 넣습니다. 2. 첫 모듈: 도메인·재료·그 모듈에 필요한 마스크와 공정으로 된 Flow를 `setFlow(flow, { agent })`로 넣고(`agent`는 [AI 생성 기록](#ai-생성-기록)에 남길 이름), `computeTo(마지막 공정 ID)`로 계산합니다. 3. 다음 모듈마다: `appendFlow({ processes, masks?, materials? }, { expectedLastStepId })` 후 `computeTo(그 모듈의 마지막 공정 ID)`. 검증한 Flow와 같은 ID를 유지하세요. 모듈마다 무엇을 했고 화면에서 무엇을 보면 되는지 한 줄로 알립니다. 진행 메시지를 포함해 사용자에게 보이는 모든 글은 사용자의 언어로 씁니다(내부 작업 메모를 영어로 남기지 마세요). 앞 모듈의 계산 결과는 이어서 사용되므로 처음부터 다시 계산하지 않습니다. 4. 마지막에 측정 표를 보여줍니다. 저장은 사용자가 원할 때만 `save()`로 하고, `{ saved: true }`를 확인한 뒤에만 저장했다고 보고합니다. `LOGIN_REQUIRED`이면 사용자가 로그인해야 합니다. ```js const api = window.arpelier; await api.setFlow({ ...verified, masks: [], processes: verified.processes.slice(0, 3) }, { agent: "Claude" }); await api.computeTo("nit"); await api.appendFlow({ masks: verified.masks, processes: verified.processes.slice(3, 8) }, { expectedLastStepId: "nit" }); await api.computeTo("strip"); ``` 모듈 사이에 `FLOW_CHANGED`가 나오면 사용자가 문서를 바꾼 것입니다. 덮어쓰지 말고 `getFlow()`로 현재 상태를 확인한 뒤 사용자에게 알리세요. 서버 계산 한도는 [계산 한계](/docs/agents/performance.md#서버-계산의-범위)를 확인하세요. ## 완료 판정 체크 | 수행한 작업 | 확인해야 할 결과 | 성공으로 오해하기 쉬운 신호 | | --- | --- | --- | | 흐름 수정 | 검증을 통과한 새 문서, 유지된 ID·메모, 수정된 리비전 | 내부 배열 값만 바뀜 | | 특정 단계 계산 | 요청한 단계가 최종 결과에 반영됨, 계산 종료, 오류 없음 | 클릭 이벤트 발생, 계산 시작 | | 구조 검사 | 해당 리비전·단계의 재료·단면·연결 결과 | 이전 단계의 스크린샷 | | 프로젝트 저장 | 저장 성공 응답 또는 UI 성공 상태, 변경 상태 해소 | 정보 창 닫기, 계산 완료 | | 저장본 재열기 | 같은 프로젝트·의도한 단계·실제 격자, 요청된 구조 관측의 일치 | 문서 로드, 저장 성공 응답만 확인 | | 공유 | 저장된 프로젝트 ID와 실제 접근 권한 | 게스트 `/p/...` URL 복사 | 계산 취소·실패 시 이전에 완료된 구조가 남아 있을 수 있습니다. 화면에 구조가 보인다는 이유만으로 새 입력의 계산 성공을 보고하지 마세요. 저장 실패도 계산 결과와 별도로 보고해야 합니다. ## 작성 순서 1. 현재 문서와 사용 가능한 도구·버전을 확인합니다. 2. 도메인·재료·마스크와 공정 ID를 일관되게 작성합니다. 3. 입력 검증 결과를 확인한 뒤 변경을 적용합니다. 사용자가 작성한 이름과 메모를 보존합니다. 4. 짧은 공정 접두부부터 계산해 마스크 극성과 식각·주입 대상을 확인합니다. 5. 요청한 최종 단계를 계산하고 단면·재료·연결을 확인합니다. 6. 저장이 요청된 작업은 저장 성공을 별도로 확인하고 프로젝트 종류와 링크 접근 조건을 함께 전달합니다. 근거: 웹 앱의 `ProjectHistoryManager`, 저장 컨트롤러, MCP native authoring 및 workspace 계약. 이 페이지는 비밀 키, 내부 서비스 자격 증명, 비공개 운영 경로를 제공하지 않습니다.