Engineering Paper Extension SDK
Contract-only Extension surface
이 문서의 Engineering Paper editor, template contribution, settings schema와 presentation resource는 아직 package 제공 production Flow surface에 연결되지 않은 목표 계약입니다. 현재 @px.component는 companion metadata와 annotation-derived port만 등록하고 Canvas에서는 read-only unsupported로 표시됩니다. portable V1 container는 content/component.json 선언 하나만 받으며, 다중 선언과 host presentation은 Node presentation과 third-party UI의 contract_only V2입니다. V2 Canvas는 host-owned component.declarative@2만 사용합니다. Engineering Paper의 rich editor·renderer와 non-empty settings는 RFC-014와 별도 versioned settings contract가 모두 승인되기 전에는 선언하거나 활성화하지 않습니다.
정식 디자인 registry에 이미 있는 component.engineering-paper@1과 기본 즐겨찾기는 legacy first-party built-in/oracle입니다. 이 host-owned renderer는 built-in type catalog에서만 배치되며 package가 제공하는 renderer도, successor V2·RFC-014 활성화 근거도 아닙니다. 따라서 기존 built-in의 production 제공 상태와 이 문서가 정의하는 package/editor 계약의 contract_only 상태를 합치지 않습니다.
별도로 engineering-paper-package.v1.pxpkg라는 committed local V1 test fixture가 있습니다. 이 container는 pipelinexlab/engineering_paper@2.0.0, requiredContractVersion: 1, content/component.json을 사용하며 Runtime test가 user-accepted local install → describe → resolve → execution materialization을 실행합니다. 이는 실행 가능한 V1 test fixture의 존재를 뜻하지만 published release, successor V2 declaration 또는 rich UI activation을 뜻하지 않습니다.
Engineering Paper는 선택 설치형 문서 작성 Extension의 목표 계약입니다. 이 surface는 Flow에 배치할 Engineering Paper Component 하나, 그 Node에서 열 editor, template 문법과 Document/PDF artifact를 함께 설계합니다.
Core
Python SDK ↔ PXFLOW → Run → Result
Optional Engineering Paper Extension
Engineering Paper Component
└─ editor: section · formula · evidence table
└─ artifacts: Document · PDFFormula block과 Evidence table은 editor 안의 문서 요소입니다. 별도 Component나 Canvas Node가 아닙니다.
설치와 public import
V1은 dependency 설치 CLI 문법을 승인하지 않습니다. 승인된 CLI grammar가 생기면 이 위치를 그 정확한 철자로 교체하며, 그전에는 존재하지 않는 명령을 계약이나 안내에 제시하지 않습니다.
목표 package는 두 public surface를 제공하도록 설계합니다.
from pipelinexlab import px
from engineering_paper import components as paper_componentspaper_components.engineering_paper는 package가 export할@px.componentcompanion target입니다.px.paper,px.Paper와 FormulaExpression helper는 향후 Extension 설치가 활성화할 optional typed SDK extension입니다.- package bridge가 구현되면 optional symbol을 사용할 수 있으며, 누락 진단은 필요한 package와 version을 표시합니다.
- 현재 core
@px.function,px.Results,px.Record와px.Flow구성은 Extension과 독립적으로 사용할 수 있습니다. - decorator
@px.record는 current facade에 없는 planned symbol입니다. 현재 record schema는px.Recordsubclass가 소유합니다.
계획된 @px.paper의 기능·release·manual은 Engineering Paper Extension이 소유합니다. 같은 px facade를 사용해 core type·unit·key 검사를 재사용합니다.
사용자가 기억할 한 가지 경로
완전한 최소 예제
아래 Python fence 전체는 contract_only이며 current SDK에서 실행할 수 없는 candidate입니다. core Function 부분만 현재 facade의 일반 parameter annotation과 px.Results annotated field를 사용합니다. @px.paper, document.input(...), non-empty settings={"template": ...}와 그 input 연결은 별도 versioned settings + port-projection 계약 뒤의 후보이므로 fence 전체를 current 예제로 복사하면 안 됩니다. 초기 successor V2는 component declaration.inputs에 publish-time 고정된 port와 absent 또는 {} settings만 허용합니다.
이 예제는 contract_only이며 현재 SDK에서 실행할 수 없습니다.
from pipelinexlab import px
from engineering_paper import components as paper_components
class CheckResults(px.Results):
utilization: float
passed: bool
@px.function(
key="check_load",
version="1.0.0",
description="Checks one load against the design limit.",
)
def check_load(load: float) -> CheckResults:
return CheckResults(
utilization=load / 500.0,
passed=load <= 500.0,
)
@px.paper(
version="1.0.0",
title="Load check document",
description="Presents the load check result.",
)
def load_check_document(document: px.Paper):
utilization = document.input("utilization", float, unit="1")
passed = document.input("passed", bool)
summary = document.section("summary", title="Check summary")
summary.result("utilization", utilization)
summary.result("passed", passed)
flow = px.Flow(
"load_review",
label="Load review",
description="Checks a design load and prepares a document result.",
)
load = flow.input("load", float, unit="kN", description="Design load.")
check = flow.node("load_check", check_load)
paper = flow.node(
"design_document",
paper_components.engineering_paper,
settings={"template": load_check_document},
)
flow.connect(load, check.inputs.load)
flow.connect(check.results.utilization, paper.inputs.utilization)
flow.connect(check.results.passed, paper.inputs.passed)
flow.result(
"paper_document",
paper.results.document,
description="Generated engineering document.",
)Engineering Paper Node의 public result member는 paper.results.document로 고정합니다. SDK 작성자와 사용자가 직접 보는 artifact는 Document이므로 document가 정확한 이름입니다. report는 독립 제품인 Report 개념과 충돌하므로 채택하지 않습니다. 이 결정은 같은 immutable Document artifact reference에 안정적인 member 하나를 부여할 뿐 payload를 복제하지 않으며, PDF는 계속 명시적 export일 때만 렌더링하므로 모든 Flow Run에 PDF 비용을 추가하지 않습니다.
SDK → PXFLOW 1:1 대응
| SDK | PXFLOW |
|---|---|
installed engineering-paper package · 계획 | Project dependencies의 Engineering Paper · 계획 |
paper_components.engineering_paper | Components dock의 Engineering Paper 카드 · 계획 |
flow.node("design_document", ...) | Canvas의 Engineering Paper Node |
document.input("utilization", float, unit="1") · 후보 | 별도 승인 뒤 compiler가 고정할 declaration input; Node-instance port 아님 |
flow.connect(check.results.utilization, paper.inputs.utilization) | upstream result → Node input 연결 |
summary.result(...) | editor의 mapped result 위치 |
향후 Template definition과 Component는 재사용 정의로 등록됩니다. 현재 flow.node(...)로 배치한 Component target은 read-only unsupported Node이며, flow.connect(...)의 연결과 raw settings는 보존됩니다.
초기 successor V2의 port source는 install-time component declaration.inputs뿐입니다. 향후 deterministic Paper compiler가 document.input(...)을 publish/materialization 시점에 fixed declaration input으로 낮추는 것은 별도 승인 뒤의 후보입니다. Engineering Paper 공개 inventory는 engineering_paper Component 정확히 하나이므로 input contract가 다른 template은 versioned settings + port-projection contract 전까지 unsupported입니다. 일반 package의 1:N 기능은 이 inventory를 넓히지 않습니다. Add input과 Node-instance port materialization도 같은 계약 전까지 차단합니다.
Template 문법
@px.paper가 호출하는 template 함수는 document builder를 받습니다. 예를 들어 summary = document.section(...)으로 Section handle을 받은 뒤 summary.text(...)처럼 호출합니다.
| 소유자 | 호출 | 역할 | 실행 경계 |
|---|---|---|---|
PipelineXLab SDK px | @px.paper(...) | Extension template의 key·version·title 선언 | 실행하지 않음 |
Document builder document | .input(key, type) | 후속 계약의 publish/materialization-time fixed declaration input 후보 | 초기 successor V2에서 미승인·Node-instance port로 사용하지 않음 |
Document builder document | summary = document.section(...) | 문서 구조와 Section handle 선언 | 실행하지 않음 |
Section handle summary | .text(key, ...) | 설명·가정·검토 메모 | 실행하지 않음 |
Section handle summary | .result(key, source) | mapped input의 field 표시 | 읽기 전용 projection |
Section handle summary | .table(key, source) | Table input 표시 | 읽기 전용 projection |
Section handle summary | .calculation(key, ...) | 문서 내부의 짧은 typed 수식 | Extension formula evaluator |
Section handle summary | .evidence(key, source) | evidence rows 표시 | 읽기 전용 projection |
계획된 실행 연결은 현재 Flow의 Engineering Paper Node input binding이 소유합니다. 초기 successor V2에서는 declaration에 고정된 input만 host projection이 표시하며 rich editor는 RFC-014 후속 계약 전에 활성화하지 않습니다.
Component input과 editor projection
아래 저장 projection은 current-v1-legacy-engineering-paper built-in/oracle branch의 contractSnapshot · document setting에 적용하며 successor V2에는 적용하지 않습니다. 같은 관계를 successor V2의 Python document.input(...) authoring으로 만드는 경로는 RFC-014와 별도 versioned settings + port-projection contract 뒤의 candidate입니다.
| 관계 | source | destination |
|---|---|---|
.pxflow binding | check.results.check_result | design_document.inputs.check_result |
| Component-owned document projection | check_result.utilization | summary/utilization block |
첫 줄은 공통 Flow 실행 의미입니다. 둘째 줄은 current V1 legacy built-in/oracle이 저장하는 document projection 의미입니다. successor V2에서 같은 projection을 새로 authoring하는 것은 별도 settings contract 뒤의 candidate이며, 초기 successor V2는 non-empty setting이나 Node-instance dynamic port를 저장하지 않습니다.
Engineering Paper 전용 input wire는 없습니다. 실행값은 기존 Node.inputBindings의 flowInput | nodeResult | subflowResult와 RFC-003 TypeDescriptor를 그대로 사용하는 것이 목표입니다. 다만 current V1 legacy Engineering Paper wire는 예외적으로 canonical pxflow-direct.v1.schema.json과 Rust codec의 EngineeringPaperNodeSetting { contractSnapshot, document }를 필수 저장·읽기 형상으로 사용합니다. contractSnapshot은 ResolvedComponentContractSnapshot이며 read 시 검증되고, 그 ComponentRef는 Node target과 같아야 합니다. 이 형상은 V1 전용 호환 권위이며 generic Component recovery 또는 successor V2 settings의 권위가 아닙니다.
Flow/current reader가 무조건 보존하는 경계는 saved exact ComponentRef target, raw settings와 연결 사용처에서 유도한 일부 input/result port입니다. original package bytes는 package artifact owner가 실제로 retained한 경우에만 별도 recovery evidence로 사용합니다. Flow/current reader는 ref, raw settings 또는 usage-derived port에서 package bytes를 추론하거나 재구성하지 않습니다. generic/successor V2 reader는 full/last-verified typed/declaration snapshot을 새로 저장하지 않습니다. current V1 legacy Engineering Paper setting의 persisted snapshot은 별도 호환 branch로 그대로 읽습니다.
Component release와 Document setting
계획된 Engineering Paper marketplace release는 공용 verified envelope의 packageKind: "component"를 사용하고 application/vnd.pipelinexlab.component+zip mimetype으로 교차 검사합니다. manifest 자체는 공용 package-envelope profile의 허용 member schemaVersion · packageKind · publisher · packageKey · packageVersion · requiredFeatures · requiredContractVersion · capabilities · pythonDependencies · projectSnapshot · entries를 그대로 사용합니다. required member는 schemaVersion · packageKind · publisher · packageKey · packageVersion · requiredFeatures · requiredContractVersion · entries이고, 빈 capabilities와 pythonDependencies는 생략하거나 빈 배열로 읽되 canonical serialization에서는 생략합니다. Component에는 projectSnapshot을 허용하지 않습니다.
successor V2 declaration은 requiredContractVersion: 2와 content/components/engineering_paper.json을 사용하고, declaration의 executorEntry는 manifest에 포함된 content/engineering_paper.py를 정확히 가리킵니다. 따라서 현재 V1 installer가 거부해야 하는 contract-only 형상입니다. V2가 받아들이는 Canvas presentation은 이 declaration에서 host가 만든 component.declarative@2뿐입니다. editor asset, template contribution, document codec, element contribution, read-only/print renderer와 manual entry는 RFC-014 승인 전에는 deferred이며 설치 index나 renderer registry에 넣지 않습니다. 배치 identity는 새 ID가 아니라 publisher · packageKey · packageVersion · packageDigest · releaseByteDigest · componentKey · componentContractVersion의 exact 7-field ComponentRef입니다.
따라서 현재 package 상태는 둘로 읽습니다. committed V1 test fixture는 존재하고 실행됩니다. content/components/engineering_paper.json을 쓰는 signed/published successor V2 release artifact는 아직 없으며, 이 부재를 V1 fixture 부재로 표현하지 않습니다.
따라서 7-field ComponentRef는 현재 member order만 승인된 contract-only non-resolvable shape입니다. 실제 successor V2 release bytes가 없는 동안 concrete packageDigest 또는 releaseByteDigest를 기록하거나 exact ref를 생성·설치·admit하지 않습니다.
공용 container의 detached signature가 optional이라는 규칙은 안전한 inspection 경계에 적용됩니다. 설치 admission은 Engineering Paper만의 더 강한 규칙을 만들지 않고 공용 RFC-005 channel matrix를 그대로 소비합니다. public marketplace는 publisher release signature와 fresh revocation evidence가 검증된 release만 허용합니다. unsigned local install은 사용자 명시 수락·경고·audit가, unsigned admin-attached install은 관리자 명시 수락·audit가 있는 Project installed closure에서만 허용됩니다. signature가 present-unverified, invalid, revoked이거나 verification evidence가 stale/unavailable이면 어느 channel에서도 수락으로 덮어쓰지 않고 거부합니다. signature 존재나 renderer id만으로 실행 권한을 주지 않습니다.
current V1 legacy EngineeringPaperNodeSetting은 contractSnapshot · document를 필수로 유지합니다. 반면 successor V2 declaration은 settings schema를 포함하지 않으며 Node settings는 absent 또는 빈 object여야 합니다. 아래 non-empty document-only 형상은 승인·활성화된 V2가 아니라 후보이며, RFC-014와 별도 versioned settings contract가 모두 승인되고 materializer가 두 admission을 검증하기 전에는 writer·reader·admission에 추가하지 않습니다. candidate의 document는 schemaVersion · templateRef · generation · elements의 한 canonical EngineeringPaperDocument이며, element는 elementKey · elementType · elementVersion · revision · parentKey? · payload를 가집니다. 알려지지 않은 element type/version도 payload의 JSON 의미를 손실 없이 보존하고 read-only renderer가 없으면 편집하지 않습니다. V1 codec은 schema version 1만 편집하며, 다른 version은 원본 bytes를 보존한 read-only 상태로 엽니다. Document는 16 MiB, element 10000개, nesting/payload depth 32로 제한합니다.
Editor transaction은 kind가 insert | update | remove인 closed command입니다. 모든 command는 expectedDocumentGeneration을, update/remove는 expectedElementRevision을 요구합니다. insert는 optional afterElementKey와 전체 element를 전달하고 update는 replacementPayload만 바꿉니다. command는 1 MiB로 제한하며 duplicate key, missing parent, 다른 parent의 anchor, depth overflow와 stale generation/revision을 거부합니다.
문서 내부 수식
FormulaExpression은 Engineering Paper Extension의 문서 표시·검토 수식입니다. Node에서 실행할 Python 계산은 Project-local이어도 explicit stable key가 있는 @px.function으로 작성합니다. decorator 없는 def는 Function·Component 내부 helper로 사용합니다.
calc = summary.calculation("utilization_check", title="Utilization check")
demand = calc.input("demand", 320.0, unit="kN")
capacity = calc.input("capacity", 500.0, unit="kN")
ratio = calc.expression(
"ratio",
lhs="eta",
formula=px.formula.div(demand, capacity),
unit="1",
)
calc.check("within_capacity", ratio <= 1.0)Python formula tree
Formula helper는 문자열 eval이 아니라 typed expression tree를 만듭니다. operator, operand, unit dimension과 source key를 보존해 editor, PDF와 AI가 같은 식을 읽습니다.
formula = px.formula.maximum(
px.formula.div(demand, capacity),
px.formula.div(service_demand, service_capacity),
)지원 operator와 arity는 exact engineering_paper@1.0.0 release contract로 고정합니다. V1 집합은 fixture가 실제로 사용하는 binary div와 binary maximum뿐입니다. 알려지지 않은 operator를 Python code로 실행하거나 이름이 비슷한 함수로 대체하지 않습니다.
저장 형상은 schemaVersion · root의 FormulaExpressionTree입니다. root는 kind로 구분하는 closed source · decimal · expression union이고 각각 sourceKey · unitCategory, value · unitCategory, operator · operands · unitCategory를 가집니다. 이 tree는 formula element의 payload에 저장하며 source 값은 별도 Engineering Paper wire가 아니라 ordinary Flow binding이 해석한 typed input에서 읽습니다.
source와 decimal은 RFC-003 UnitCategory의 canonical unit을 사용합니다. div는 같은 category 두 값을 dimensionless로 만들고, maximum은 같은 category 두 값의 category를 유지합니다. implicit conversion은 없으며 저장한 result category가 inference와 다르면 실행을 거부합니다. 한 root는 512 node와 depth 32를 넘지 않습니다. Runtime은 기존 RepositoryAdapterPort<ReadFlow>와 FlowRevisionExecutor 경계에서 exact saved Flow revision을 읽고 claimed attempt마다 evaluator contribution을 한 번 호출합니다.
missing source, unknown operator, shape/arity, unit, division-by-zero, complexity와 decimal resource refusal은 각각 기존 PX_FORMULA_REFERENCE_NOT_FOUND, PX_FORMULA_OPERATOR_UNSUPPORTED, PX_FORMULA_SHAPE_INCOMPATIBLE, PX_UNIT_INCOMPATIBLE, PX_FORMULA_DOMAIN_ERROR, PX_FORMULA_COMPLEXITY_LIMIT, PX_RESOURCE_LIMIT로 fail closed합니다. Formula numeric bound는 값을 다시 선언하지 않고 RFC-003 numeric envelope의 decimal bound·canonical grammar·int64/float64 serialization과 Report-owned computation/display scale을 member reference로 채택합니다. RFC-003가 승인한 calculation_trace type과 Engineering Paper가 그 type을 채우는 계약은 별개입니다. 후자의 shape와 FormulaExpression mapping은 product owner가 직접 정할 reserved 범위이며 이 문서는 정의하지 않습니다.
key와 instance identity
| key | 예 | 소유자 |
|---|---|---|
| template key | load_check_document | Extension SDK source |
| Node key | design_document | current Flow |
| input port key | check_result | template/Component contract |
| document block key | summary | template 또는 Node editor |
| result field key | utilization | upstream result type |
같은 template을 여러 번 배치하는 목표 계약에서는 editor instance를 template key가 아니라 Flow target + nodeKey로 구분합니다.
Draft와 artifact
아래 lifecycle은 current-v1-legacy-engineering-paper built-in/oracle branch에만 적용합니다. successor V2의 Node editor와 document setting은 RFC-014와 별도 versioned settings contract 뒤의 candidate이며, 현재 이 lifecycle을 V2에 적용하지 않습니다.
| branch | successor V2 적용 | 객체 | 생성 시점 | 저장 위치 |
|---|---|---|---|---|
| current-v1-legacy-engineering-paper | false | Document draft | Node editor에서 Save | Flow revision의 Component-owned Node setting |
| current-v1-legacy-engineering-paper | false | Document artifact | 저장된 Flow를 Run | immutable Result/artifact store |
| current-v1-legacy-engineering-paper | false | PDF artifact | Document artifact에서 Export PDF | immutable artifact store |
Document와 PDF는 Engineering Paper Extension이 소유하는 artifact입니다. Document는 Flow Run의 Node result이고 PDF는 사용자가 그 Document에서 명시적으로 출력합니다. Project의 .pxreport Report는 px.Report 또는 Report Workbench에서 작성·저장합니다. Engineering Paper의 Document·PDF artifact와 Project Report는 서로 다른 문서입니다.
paper.results.document의 wire value는 payload가 아니라 EngineeringPaperArtifactRef 하나입니다. 이 ref는 opaque handle이나 UUID 없이 artifactKey · contentDigest만 가지며, 같은 ref는 공통 불변 artifact store에서 항상 같은 bytes를 가리킵니다. Document artifact는 version 1 Document와 sourceFlowRevision · nodeKey · evaluatorRelease · inputDigest provenance를 함께 보존합니다. evaluatorRelease는 별도 문자열이 아니라 exact ComponentRef입니다.
PDF는 schemaVersion · artifactRef · sourceDocumentRef · exportProfileDigest의 파생 DTO로 저장하고 PDF bytes는 DTO나 Node result에 inline하지 않습니다. Document 저장은 기존 artifact.create, PDF 저장은 artifact.create와 artifact.export, 읽기는 artifact.read capability와 active exact-project AccessGrant를 요구합니다. local profile은 SQLite metadata와 공통 FilesystemCas를 사용하며 object reachability가 유지되는 동안 보존합니다. V1은 implicit expiry, delete 또는 purge operation을 만들지 않습니다. Report의 PDF repository와 .pxreport는 이 경로에 사용하지 않습니다.
Paper artifact와 Project Report를 연결해야 할 때는 neutral ArtifactRelationship이 paperArtifactRef · reportRef를 함께 보관합니다. Paper aggregate는 Report identity를 저장하지 않고, Report aggregate도 Paper identity를 저장하지 않습니다. reportRef는 Report가 소유한 PublicDigestRef를 그대로 사용합니다.
missing dependency와 호환성
이 절의 partial recovery는 generic Component와 successor V2 경계입니다. current V1 legacy Engineering Paper setting의 contractSnapshot · document 필수 wire와 검증은 계속 유지됩니다.
| 상태 | 진단 | 복구 동작 |
|---|---|---|
| exact package 없음 | PX_PACKAGE_NOT_INSTALLED | Add required package |
| Component contract 불일치 | PX_COMPONENT_CONTRACT_MISMATCH | Review update |
| target resolve 실패 | PX_COMPONENT_UNRESOLVED | dependency·lock 확인 |
현재 reader는 복구 전에도 exact ComponentRef, raw settings와 usage-derived 일부 port를 읽기 전용으로 보존합니다. original package bytes는 package artifact owner가 실제로 retained한 경우에만 recovery evidence로 사용하며 Flow 상태에서 추론하지 않습니다. generic/successor V2에는 full last-verified contract snapshot owner가 없으며, 비슷한 이름의 package나 latest target으로 자동 재연결하지 않습니다. current V1 legacy snapshot을 generic recovery authority로 승격하지 않습니다.
Contract-only 검토 체크리스트
- Engineering Paper가 Components에서 발견되는가?
- package가 Canvas Component를 Engineering Paper 하나로 제공하는가?
- Formula block·Evidence table이 editor 내부 요소로 보이는가?
- SDK가 normal import +
flow.node(...)placement를 사용하는가? - editor가 현재 Node input만 mapping하는가?
- Document·PDF가 Component artifact로 표시되는가?
- 같은 template의 여러 Node instance를
nodeKey로 구분하는가?