SDK Adapter 체계
현재 production third-party Component 경계
아래의 current V1 installer·catalog·executor 관찰은 내부/runtime 감사 사실일 뿐, production third-party Component의 install admission 또는 activation 권한이 아닙니다. 현재는 안전한 inactive quarantine이 없습니다. exact ProjectScope { kind: "project", workspaceKey, projectKey } data-path isolation은 구현됐지만, authenticated-principal authorization·execution-trust closure와 production gate가 닫힐 때까지 third-party Component install admission과 activation을 모두 차단합니다.
후속 V2 Canvas는 host-owned component.declarative@2만 사용합니다. settings는 component-declared-settings@1로 선언당 최대 8개까지 받고 host control registry가 그리며 package JS·HTML·CSS·React·iframe UI를 마운트하지 않습니다. Installed Component editor/viewer는 위 두 gate가 닫힌 뒤에도 RFC-014와 별도 versioned settings contract가 승인한 successor에서만 엽니다. 이 차단은 current Function과 built-in/first-party 경로를 변경하지 않습니다.
category와 format 선택하기
Adapter category는 Extension이 제공하는 화면·data source·artifact·codec의 공통 동작을 분류하는 계약입니다. Adapter가 있다고 Extension이 core 제품 탭이 되지 않습니다. 하나의 Python Component package/distribution은 하나 이상의 Component symbol을 export할 수 있고, 배치된 Component Node는 Adapter가 아니라 Component target 경로를 사용합니다. 별도의 portable Component container V1 reader는 container마다 단일 content/component.json 선언만 받습니다.
FUTURE V2: Component Node 표현과 서드파티 UI와 node-presentation-profile.v2.json은 portable container 하나에 여러 Component 선언을 담는 requiredContractVersion: 2 successor를 contract_only로 설명합니다. 그 V2의 Node projection은 기존 component.signed@1 재사용이 아니라 첫 contract인 contract-only component.declarative@2이며, value schema는 component-declarative-node-value.v1.schema.json의 schemaVersion: 1입니다. 이 renderer version은 Component container contract V2·descriptor schema V2·componentContractVersion과 독립이고, current production design-system catalog에는 등록되지 않았습니다. 현재 V1 container reader 계약이 아닙니다. V2 Canvas는 host-owned projection만 그리며 settings를 component-declared-settings@1로 선언당 최대 8개까지 받고 host control registry가 그립니다. package UI는 Canvas React tree에 들어오지 않고, Installed Component editor는 RFC-016의 component-rich-surface-declaration@1이 소유하는 격리 surface입니다.
portable V1 declaration은 classification: "flow-node" 하나와 ports·executor·container-carried, integrity-measured declarative presentation을 소유합니다. authoring ComponentDescriptor의 category는 V2 선언과 같은 닫힌 primary task registry(input, transform, calculate, review, deliver, control)이고 subcategory는 그 아래 publisher가 정하는 열린 key이며, .pxflow componentNode.settings는 placement-local instance 값입니다. 세 authority의 member를 서로 옮겨 쓰지 않습니다.
현재 public SDK는 input/result port의 type을 일반 Python parameter annotation과 px.Results subclass의 annotated field에서 파생하고, px.input·px.result·px.setting marker가 그 선언부에 description·unit을 더합니다. Component settings schema는 px.Settings subclass가 선언합니다. decorator @px.record, px.field, px.Table, px.Artifact는 현재 facade에 없습니다. flow.node(..., settings={...})는 선언된 Component면 그 선언에 대조해 값을 판정하고, 선언이 없는 기존 object는 그대로 저장한 뒤 container에 별도로 포함한 executor의 execute(inputs, settings)에 전달합니다. decorator body에서 그 executor를 자동 생성하는 build bridge는 없습니다.
app_surface: 사용자가 열고 상호작용하는 화면data_source: 값을 읽는 외부 데이터 sourceartifact: PDF, image 같은 engineering artifactdocument_codec: 문서 가져오기·내보내기
| category | 공개 SDK 작성 경로 | 소유하는 계약 |
|---|---|---|
app_surface | @px.app 또는 signed package surface metadata | App entry·layout·Flow projection |
data_source | 해당 format의 typed Extension definition | source selection·read contract |
artifact | 해당 format의 typed Extension definition | artifact reference·preview contract |
document_codec | 해당 format의 typed Extension definition | import·export contract |
각 category는 closed typed schema를 사용합니다. 임의 descriptor JSON이나 열린 설정 map으로 category별 계약을 우회하지 않습니다.
그다음 category가 지원하는 formatKey를 고릅니다. 이 adapterCategory + formatKey 조합이 Local, SaaS, UI와 MCP에서 같은 lifecycle·권한 계약을 사용하게 합니다.
V1에 등록된 format 목록은 app_surface의 pxflow_app, streamlit뿐입니다. 아래 data_source, artifact, document_codec 항목은 category의 용도를 설명하는 아직 등록되지 않은 예이며, 지원 format 목록이나 configuration schema가 아닙니다. 세 category의 registered format 목록과 closed configuration schema map은 빈 값이 V1 baseline이고, 등록되지 않은 format 요청은 비슷한 이름으로 대체하지 않고 거부합니다.
예를 들어 Streamlit은 app_surface category의 streamlit format으로 등록합니다.
category = app_surface
format = streamlitcategory는 기능의 공통 동작을 정하고, format은 그 동작을 구현하는 구체 형식을 정합니다. 독립 Streamlit app은 app_surface를 사용하고, 배치한 Component Node의 schema 없는 settings object는 .pxflow componentNode에 남습니다. Component Settings는 향후 UI owner입니다.
공통 envelope와 category별 계약
모든 adapter descriptor는 다음 공통 정보를 가집니다.
| 항목 | 의미 |
|---|---|
adapterCategory | app_surface, data_source, artifact 또는 document_codec |
formatKey | category 안에서 사용하는 구체적인 형식 |
categoryContractVersion | category의 공통 lifecycle·권한 계약 version |
formatContractVersion | 해당 format 설정 계약 version |
| semantic key | 사용자가 읽고 AI가 선택하는 안정적인 key |
title, description | 사용자와 AI가 기능의 목적을 이해하는 설명 |
| source reference | 검증·서명된 package 또는 immutable artifact revision |
| host capability | host에 요청할 수 있는 기능의 상한 |
| descriptor digest | canonical descriptor 변경을 확인하는 digest |
공통 envelope 아래에는 category별 closed schema payload를 둡니다.
AI도 이 category에 맞는 operation을 선택합니다. app_surface는 열거나 표시하고, data_source는 값을 읽으며, document_codec은 가져오기·내보내기를 수행합니다.
App Surface Adapter category
App Surface Adapter는 독립적으로 열고 실행하는 App의 공통 계약입니다. 배치한 Node의 instance settings는 .pxflow componentNode가 소유하고, Component Settings는 향후 UI owner입니다. portable declaration의 presentation은 별도 계약입니다.
예를 들어 @px.app은 exported Flow object를 참조하는 surface definition을 등록합니다. decorator는 Canvas Node를 배치하거나 Flow를 실행하지 않습니다.
from pipelinexlab import px
from structural_checks.flows.girder_design import flow as girder_design_flow
@px.app(flow=girder_design_flow, title="Girder design")
def girder_design_app(app: px.App):
main = app.page("main", title="Design")
main.input(girder_design_flow.inputs.span)
main.result(girder_design_flow.results.resistance_summary)| 구분 | 사용자가 보는 위치 | 편집 원본 |
|---|---|---|
app_surface + pxflow_app | Project Home의 App 항목, App Builder·Preview | @px.app source 또는 PXFLOW-native App direct object |
app_surface + streamlit | Project Home의 App 항목, Streamlit 화면 | signed package metadata와 Python UI source |
공통 AdapterDescriptor는 category·format·source·권한·도움말을 묶는 검색 envelope입니다. 그 안의 AppSurfaceDescriptor가 화면 entry와 semantic target을 설명합니다. 둘 다 Report 본문, Flow 내부 구조, Run이나 Result를 복사하지 않으며, 각 원본 제품에서 내용을 편집합니다.
공통으로 관리하는 항목:
- Project Home과 Workbench에서 검색하고 여는 방법
- route와 session scope
- 로그인, license, Project 권한 확인
declared | preparing | ready | active | suspended | closing | closed | failed상태- theme, locale과 접근성 context 전달
- 도움말과 package version 표시
- resource budget, health와 복구 상태
- AI가 읽는 목적, target과 지원 operation
위 여덟 state 이름은 향후 App Surface contract가 다룰 vocabulary입니다. V1은 lifecycle state machine, authorization matrix, timeout과 resource budget을 제공하지 않습니다. state 이름으로 transition이나 activation을 추론하지 않으며, activation 요청은 app_surface_lifecycle_unavailable_v1로 거부합니다. lifecycle은 후속 release의 App Surface category owner, authorization은 후속 application service, timeout/resource budget은 후속 host contract가 각각 소유합니다.
format이 소유하는 항목:
| format | format 구현이 담당하는 것 | 원본 의미를 소유하는 객체 |
|---|---|---|
pxflow_app | Builder page·layout과 Flow input/result projection | App Surface direct object와 FlowDocument |
streamlit | Python UI process, widget 화면과 health bridge | FlowDocument, ResultSnapshot |
Streamlit runtime owner의 public term은 이 guide에서 확정하지 않습니다. Plan 23은 RunRecord, 다른 public Runtime 문서는 Run을 사용하므로 owner authority가 하나의 term을 승인할 때까지 둘 다 descriptor owner 표에서 제외합니다. descriptor는 어느 쪽 payload도 복제하지 않습니다.
App Surface Adapter는 위 core object를 semantic reference로 연결합니다. Report body, Flow 내부 구조, port schema, Run ledger와 ResultSnapshot은 각 원본 객체가 계속 소유합니다.
PXFLOW App surface
PXFLOW App은 @px.app surface definition 또는 Studio Builder direct object로 정의합니다. SDK와 Builder는 같은 pxflow_app direct object를 만들고 existing Flow와 port schema를 참조합니다.
surfaceKey는 girder_design_app, target은 exact flowKey입니다. inputs와 results의 generated typed reference가 exact portKey를 가리킵니다. 독립 .pxflow는 이 direct definition을 appSurfaces에 포함해 단일 원본으로 저장합니다.
Component는 Adapter가 아닌 별도 target 경로를 사용합니다
Canvas에 배치하는 Component는 Adapter category를 만들지 않습니다. portable V1 container의 단일 content/component.json 선언은 container-carried, integrity-measured declarative icon과 summaryResultKeys를 보존하지만, 현재 production Flow reader는 이를 component.signed@1에 연결하지 않습니다. package가 export한 Component definition을 flow.node(...)로 배치한 target은 exact identity를 보존한 read-only unsupported diagnostic으로 표시됩니다. design-system registry entry는 이 reader의 live renderer가 아닙니다.
다음 installer·resolver·worker 설명은 current V1 내부/runtime 감사 관찰입니다. installer는 측정한 release와 closed grammar로 판정한 declaration을 저장합니다. install 후 product.component.resolve가 두 record를 exact 7필드 ComponentRef로 결합하고 closed target grammar로 다시 판정합니다. execution worker의 pinned release·executor source digest 재검증은 별도 단계입니다. 이 join과 declaration→Flow renderer bridge는 모두 구현돼 있습니다. 이 저장·결합 경로의 존재는 production third-party Component install admission이나 activation을 허용하지 않습니다.
현재 installer는 trust authority가 없을 때 signed Component를 기록 전에 거부하고, 명시적으로 수용한 unsigned Component만 absent·not-checked로 기록합니다. verified marketplace channel이 signature를 실제로 검증하고 기록한 release/declaration만 verified 또는 signed로 부릅니다. component.signed@1은 registry identifier일 뿐 signature 검증 증거가 아닙니다.
현재 Component contribution storage·catalog·detail·execution read는 install operation이 받은 exact Workspace·Project pair를 보존합니다. Python descriptor registry는 여전히 process-wide (kind, key)를 사용하고 resolver는 scope와 componentKey만 받으므로 같은 key의 measured release가 둘 이상이면 모호성을 거부합니다. data-path isolation은 구현됐지만 package-scoped collision-free lookup과 principal-to-Project authorization은 구현된 기능으로 설명하지 않습니다.
from inspection_pack.components import result_review
review = flow.node(
"result_review",
result_review,
settings={"layout": "compact"},
)
flow.connect(
resistance_check.results.governing_utilization,
review.inputs.utilization,
)settings={...}는 schema 없는 Node instance object입니다. Component Settings는 향후 UI owner이며, 현재 public SDK가 label·default·choices를 포함한 schema를 제공하거나 production Flow reader의 unsupported 경로가 편집 surface를 활성화했다고 주장하지 않습니다. standalone app surface, data source나 artifact codec처럼 Flow 밖의 host 기능이 필요할 때 Adapter category를 사용합니다.
Streamlit surface
Streamlit 화면은 package metadata에 App Surface 하나를 선언합니다.
[tool.pipelinexlab.surfaces.bearing_check]
format = "streamlit"
format-contract = 1
title = "Bearing check"
description = "Interactive bearing pressure check."
entry = { kind = "python_module", module = "structural_checks.apps.bearing" }
targets = [{ kind = "flow", key = "bearing_review" }]
host-capabilities = []table 이름 bearing_check가 surfaceKey입니다. surfaces table에서 adapterCategory="app_surface"가 파생되고, format="streamlit"이 format 구현을 선택합니다.
PipelineXLab이 호스팅하는 Streamlit surface를 포함한 모든 공개 Python 호출은 px.Client(workspace="...", project="...")로 Workspace와 Project 범위를 명시합니다. lowercase px.client()는 host scope 주입을 검토할 후속 계약에 예약된 이름이며 현재 facade에서 호출할 수 없습니다. input/result와 unit은 선택한 Flow schema에서 읽습니다.
기계 판독 descriptor
Build는 사용자 선언과 core object에서 canonical AppSurfaceDescriptor JSON을 생성합니다.
{
"$schema": "https://schemas.pipelinexlab.com/app-surface-descriptor/v1.json",
"descriptorVersion": 1,
"adapterCategory": "app_surface",
"categoryContractVersion": 1,
"surfaceKey": "bearing_check",
"formatKey": "streamlit",
"formatContractVersion": 1,
"title": "Bearing check",
"description": "Interactive bearing pressure check.",
"source": {
"kind": "package",
"packageKey": "structural_checks",
"packageVersion": "2.3.0"
},
"entry": {
"kind": "pythonModule",
"module": "structural_checks.apps.bearing"
},
"targets": [
{
"kind": "flow",
"scope": {
"kind": "project",
"workspaceKey": "structural_team",
"projectKey": "bridge_package"
},
"flowKey": "bearing_review",
"revisionRef": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
],
"hostCapabilities": [],
"descriptorDigest": "sha256:e18a9b12ce83528cd4be048b3203e3eb4bfc97eec8b9115be4d2c2ace6caedfa"
}PXFLOW App은 formatKey="pxflow_app", entry.kind="flowApp", target이 exact flowKey입니다. descriptor는 해당 App Surface definition과 immutable Flow revision을 찾는 semantic reference를 가집니다.
V1 canonical field order는 위 JSON의 14개 field 순서입니다. descriptorDigest는 자기 자신을 제외한 앞의 13개 field 전체를 exact projection으로 만든 뒤 RFC 8785 JCS bytes에 SHA-256을 적용한 sha256:<lowercase hex>입니다. $schema도 preimage에 포함되며 unknown field, field 누락, 순서가 다른 reviewable JSON과 digest mismatch는 거부합니다.
source는 signed package release 또는 exact Flow revision의 closed union이고, targets는 RFC-002 project scope와 exact revisionRef를 가진 Flow target 하나입니다. 두 V1 format 모두 한 Flow만 대상으로 하므로 다중 target은 거부합니다. 이 one-target 경계는 descriptor 검증과 authorization fan-out을 O(1)로 유지합니다. 문자열과 semantic key 상한은 RFC-003과 RFC-002의 기존 공통 한계를 그대로 사용해 새 크기 축을 만들지 않습니다.
App Surface capability 이름과 승인 행렬은 아직 승인되지 않았으므로 accepted V1 descriptor의 hostCapabilities는 빈 배열만 허용합니다. non-empty capability 요청은 lifecycle·authorization 계약이 승인될 때까지 fail closed합니다. 정확한 descriptor 계약은 파일 형식과 저장에도 같은 형태로 고정되어 있습니다.
signed registry와 trust
Format resolution은 descriptor 자체를 권한으로 사용하지 않습니다. 제품이 서명한 registry record가 schemaVersion, adapterCategory, formatKey, categoryContractVersion, formatContractVersion, descriptorDigest, releaseByteDigest, signature 순서로 exact format release를 고정하고, 현재 Project dependency closure에 그 signed release가 설치되어 있어야 합니다. record signature는 signature를 제외한 앞의 일곱 field의 RFC 8785 JCS bytes를 서명합니다.
서명과 trust는 별도 방식을 만들지 않고 accepted RFC-005 계약을 그대로 사용합니다. algorithm은 Ed25519이고 key는 product-managed trust store에서 resolve하며, validity window가 있는 signed revocation list가 unavailable, not-yet-valid 또는 expired이면 resolution을 거부합니다. revoked key, descriptor/release digest mismatch, 설치되지 않은 release와 unknown contract major도 유사 format으로 fallback하지 않습니다. registry lookup은 exact four-member tuple 한 번과 digest/signature 검증으로 제한되며, 설치 package 수에 비례하는 source scan을 하지 않습니다.
Desktop/Web Host Adapter
Desktop와 Web의 current Function과 built-in/first-party surface는 Project Home·PXFLOW에서 같은 이름과 계약을 사용합니다. production third-party Component의 Installed editor/viewer는 authenticated-principal authorization과 execution-trust closure 뒤에도 RFC-014와 별도 versioned settings contract가 승인한 successor에서만 제공합니다. Host Adapter는 해당 화면이 현재 환경에서 사용할 수 있는 파일 picker, 로그인, route와 허용된 host capability를 연결합니다.
App Surface가 특별한 host 기능을 요구하지 않으면 descriptor에는 기존 계약대로 빈 목록을 저장합니다.
{
"hostCapabilities": []
}사용자가 컴퓨터 파일 위치를 고르면 application command에는 raw path 대신 trusted host가 발급한 선택 참조만 전달합니다.
{
"kind": "selectedFile",
"fileSelectionRef": "<trusted host-issued file selection>",
"overwrite": "replace"
}| 화면에서 하는 일 | Host Adapter가 하는 일 | 원본 제품이 계속 하는 일 |
|---|---|---|
| 파일 picker에서 위치 선택 | fileSelectionRef 발급·실제 path 보호 | export 의미, validation과 provenance 결정 |
| Project/App 열기 | session route와 로그인 연결 | Project ACL·license·surface 상태 검사 |
| Desktop 또는 Web에서 surface 실행 | 허용된 capability와 process placement 제공 | 같은 descriptor, Flow revision과 Runtime 계약 사용 |
Host Adapter는 사용자가 별도 SDK decorator로 만드는 Component가 아닙니다. platform의 host 구성과 runtime이 소유하며, package의 hostCapabilities는 요청 가능한 상한일 뿐 자동 권한이 아닙니다. Desktop/Web 차이를 이유로 Flow, Report, artifact schema나 validation을 다시 구현하지 않습니다.
AI와 MCP가 이해하는 방식
AI는 descriptor의 category, format과 semantic key를 읽어 다음 순서로 대상을 찾습니다.
검색과 설명은 공통 envelope, category별 typed payload, manual resource와 현재 사용 가능 여부를 다루는 두 단계입니다. Plan 23의 adapter_* 예와 Plan 24의 app_surface_* 예는 서로 다른 command 이름과 signature를 제시하므로 어느 쪽도 accepted public command로 등록하지 않습니다. 정확한 command 이름과 input/output schema는 별도 AI Tool authority가 승인할 때까지 미승인입니다.
app_surface + pxflow_app을 편집할 때는 SDK-linked source-safe App command 또는 PXFLOW-native direct-object command를 사용하고, 실행은 기존pxflow_run을 사용합니다.app_surface + streamlit이 노출한 계산을 실행할 때는 descriptor의 Flow target을 확인한 뒤 기존pxflow_run을 사용합니다.data_source는 별도의 typed data-source command를 사용합니다.- 새 format도 category가 정한 typed domain operation을 사용합니다.
Adapter catalog는 search와 describe를 담당하고, 실행 권한은 기존 domain operation이 다시 검사합니다. exact type, unit과 result 설명은 FunctionDescriptor와 Flow interface에서 읽습니다.
SQLite와 immutable content의 현재 V1 경계
Local/shared SQLite adapter는 모두 PRAGMA foreign_keys=ON을 설정하지만 private schema의 foreign key 수는 0입니다. V1 reference enforcement는 adapter-managed이고 이 pragma의 relational effect는 없습니다. Constraint나 cascade를 새로 추가하지 않고 pragma도 제거하지 않습니다.
Shared SQLite contention contract는 waitCeilingMilliseconds, retryAboveDriver, totalOperationLatencyMilliseconds, concurrencyTarget, measurementProfile 다섯 dimension만 고정하고 값은 모두 deferred입니다. 관측된 busy_timeout=5000ms는 구현 증거이며 승인된 budget이 아닙니다.
V1은 immutable-content application port를 새로 만들지 않습니다. Local FilesystemCas와 shared SharedContentStore는 기존 concrete convention을 유지하고 ObjectCorrupt/ObjectUnreachable과 ContentCorrupt/ContentUnreachable의 concrete error 이름 차이도 유지합니다.
새로운 format을 추가하는 방법
새 format은 다음 순서로 플랫폼에 한 번 추가합니다.
- 기존 category가 목적에 맞는지 확인합니다.
- category의 format configuration schema를 닫힌 version으로 정의합니다.
- host driver가 공통 lifecycle, route, theme·locale, status와 dispose 계약을 구현합니다.
- Local과 SaaS에서 같은 descriptor fixture와 상태 전이를 검증합니다.
- sandbox, CSP, network, file, secret과 resource negative test를 통과합니다.
- AI
search → describe결과가 source parsing 없이 같은 semantic target을 가리키는지 확인합니다. - signed platform adapter registry의 allowlist에 format을 추가합니다.
registry는 플랫폼이 서명하고 conformance를 통과한 format driver만 활성화합니다. 같은 formatKey 재정의나 package 안의 미등록 executable은 검증에서 거부합니다. third-party format도 같은 conformance와 signing 경계를 적용합니다.
새 library가 기존 format의 단순 dependency라면 기존 format의 package dependency로 등록합니다. 예를 들어 Streamlit component library는 streamlit surface의 dependency입니다.
category를 새로 추가하는 기준
다음 조건을 모두 만족할 때만 새 adapter category를 만듭니다.
- 기존 category와 lifecycle 또는 semantic operation이 근본적으로 다릅니다.
- 기존 payload에 optional field를 계속 추가하면 invalid state를 허용하게 됩니다.
- 독립적인 권한·sandbox와 conformance suite가 필요합니다.
- AI가 기존 category와 다른 operation으로 구분해야 합니다.
library 이름이나 UI만 다른 경우에는 기존 category와 format의 dependency·configuration을 사용합니다.
공학 계산 library는 어떻게 관리하나요?
NumPy, SciPy와 분야별 계산 library는 Adapter가 아니라 Function package dependency입니다. 계산은 @px.function 안에서 사용하고 dependency lock, SBOM과 signed release가 exact version을 고정합니다.
이 분류를 사용하면 AI가 계산 방법, 사용자 화면, 데이터 source와 파일 변환에 맞는 operation을 선택할 수 있습니다.
등록 검증에서 차단할 구조
- library별 최상위 계약(
StreamlitAdapter,EngineeringPaperAdapter) - 모든 category를 합친 arbitrary
configmap - Function port, Flow 내부 구조 또는 Report block의 Adapter 복제본
- UI source, session key, label 또는 filesystem path 기반 AI 주소
- format driver가 등록하는 임의 MCP Tool 또는 자동 권한
- 같은 format에 대한 Local·SaaS descriptor 불일치
- unknown category·format의 유사 library 대체