본문으로 건너뛰기

AI·LLM 작성 계약

이 문서는 AI가 Project Function, Flow와 Report를 만들거나 고칠 때 사용하는 정식 authoring profile을 고정합니다. 사람이 작성하는 Python SDK, PXFLOW Studio, Report Workbench와 AI는 같은 semantic key와 typed command를 사용합니다. 화면 좌표, label, 최근 연 Project 또는 문서의 배열 순서를 의미로 추측하지 않습니다.

기계가 읽는 요약은 authoring-profile.v1.json이며, exact Python 문법은 Python SDK 문법, 저장 형태는 파일 형식과 저장이 소유합니다. 세 문서가 다르면 배포를 중단하고 같은 contract version에서 함께 수정합니다.

LLM이 기억할 한 가지 모델

text
decorated target                      Flow placement                 Report mapping
@px.function(key=...) / @px.component(key=...) ─▶ flow.node(...) ─┐
                                      │                     │
                               flow.connect(...)            │
                                      │                     ▼
                                 Flow public input/result ◀─ connection.bind/show

                                                        report.save()
  • @px.function@px.component는 실행 가능한 target 계약을 정의합니다.
  • decorator 없는 def는 target 구현을 나누는 helper이며 Canvas Node로 배치하지 않습니다.
  • flow.node(...) 한 번이 PXFLOW Studio의 Node 하나를 만듭니다.
  • flow.connect(...) 한 번이 두 port 사이의 Canvas connection 하나를 만듭니다.
  • Report는 Flow 내부 Node를 찾지 않고 public input과 named public result만 연결합니다.

Stable key와 Python 이름

실행 가능한 definition은 Python symbol과 별도의 stable key를 명시합니다.

python
@px.function(
    key="check_resistance",
    version="1.0.0",
    description="Checks demand against resistance.",
)
def calculate_resistance(...):
    ...

calculate_resistance를 리팩터링해 이름을 바꿔도 check_resistance identity는 유지됩니다. stable key를 바꾸려면 RenameSemanticKey command로 사용처와 영향을 함께 계획합니다. LLM은 일반 text replacement로 key를 바꾸지 않습니다.

Report의 sectionKey, blockKey, valueKey, tableKey, attachmentKey, slotKey, connectionKeymappingKey도 label·좌표·순서에서 만들지 않습니다. block·value·table·slot 계열 key는 Report 전체에서 유일합니다.

Project Function 만들기·수정·분기

Project가 소유한 @px.function(key=..., version=...)만 제품 authoring target입니다. 설치한 dependency package의 Function과 Component는 consumer Project에서 계약을 조회하고 Node로 배치할 수 있지만, source 수정은 owning package의 source와 release 과정에서 수행합니다.

Function 변경은 AI와 MCP 연결의 Project Function 작성에 정의한 function_validate_changefunction_plan_changefunction_apply_change 경로를 사용합니다. command 이름과 의미는 Project Function command가 소유합니다.

사용자 의도typed command
새 공유 Function 만들기CreateProjectFunction
사용 중 Function 수정하기CreateFunctionVersion + referencePolicy: "all_current_usages"
특정 Node만 다르게 만들기BranchProjectFunction + 해당 Node의 ReplaceNodeTarget

Function 수정은 기존 version의 source를 덮어쓰지 않습니다. 새 immutable Function version 후보를 만들고, 현재 Project에서 사용하는 모든 Node, 새로 materialize할 Flow revision과 그 Flow를 pin한 Report connection을 plan impact에 나열합니다. 사용자가 승인한 reference update는 Function source·version과 함께 한 transaction으로 적용합니다. 일부 reference만 갱신하려면 edit이 아니라 branch를 사용합니다. 기존 immutable Flow·Report revision과 published version은 history로 보존합니다.

LLM은 line-based raw source patch를 만들거나 Python 함수 이름·파일 순서로 definition을 찾지 않습니다. explicit decorator key, exact base FunctionRef와 source digest를 사용해 complete typed Function draft를 제출합니다. Plan이 보여 준 syntax-aware diff를 적용하고 Function·Flow를 다시 materialize한 semantic contract가 plan과 같을 때만 Apply가 성공합니다.

지원하는 Python 작성 범위

Flow discovery가 실행 결과나 환경에 따라 달라지지 않도록 다음 profile을 사용합니다.

  1. Flow module은 module-level public flow = px.Flow(...) 하나를 export합니다.
  2. flow.input, flow.node, flow.connect, flow.result, flow.group, flow.subflow 선언은 module top level에 결정적인 순서로 둡니다.
  3. Node target body와 helper 안에서는 일반 Python 계산을 사용할 수 있습니다.
  4. Flow 구조를 만드는 조건문, 환경 변수 분기, network 호출, 무작위 key, import-time 저장·실행은 authoring profile에 포함하지 않습니다.
  5. 반복되는 배치는 생성 loop로 숨기지 않고 stable nodeKey가 보이는 선언으로 작성합니다.
  6. report.save()client.run(...)은 import 시 실행하지 않고 명시적 script entry 또는 authoring command에서 호출합니다.

이 범위를 벗어난 Python은 일반 프로그램으로 실행할 수 있어도 Studio의 source-safe 왕복 편집 대상으로 승인하지 않습니다. Validator는 지원하지 않는 구문 위치와 안전한 대안을 구조화된 Diagnostic으로 반환합니다.

Report를 새로 만들고 다시 열기

새 Report는 px.Report(...), 저장된 Report는 px.Report.open(...)으로 시작합니다.

python
report = px.Report.open(
    "girder_report",
    workspace="engineering",
    project="bridge_project",
    revision="sha256:report-revision-digest",
)

open은 exact base revision과 generation을 draft에 고정합니다. report.save()는 이를 precondition으로 검사하고 새 .pxreport revision을 만듭니다. Workbench나 다른 AI가 먼저 저장했다면 apply/save는 원자적으로 실패하며 최신 revision과 의미 diff를 이용해 새 plan을 만듭니다.

Python SDK, Workbench와 AI는 다음 source와 target을 모두 같은 typed command로 표현합니다.

방향허용되는 closed union
Report → Flow inputauthored value·Record/List, stable row/field를 선택한 Table, Attachment
Flow result → Reportscalar/Record/List result slot, result table, Artifact slot

connection.bind(mapping_key, ...)connection.show(mapping_key, ...)는 stable mapping key를 필수로 받습니다. Record와 Table의 field·row key 대응은 명시하며 현재 열 순서를 사용하지 않습니다.

기존 Report도 같은 command surface로 바꿉니다. px.Report.open(...)으로 exact base를 연 뒤 같은 key의 같은 kind를 다시 선언해 내용을 갱신하고, 위치 변경은 report.move(...), 항목 제거는 report.remove(...), mapping 제거는 connection.remove_mapping(...), Connected Flow 제거는 report.remove_connection(...)을 사용합니다. LLM도 각각 대응하는 typed command를 plan하며 배열 index 조작이나 raw JSON patch를 만들지 않습니다.

Batch의 input과 result merge

Batch case input은 connection에 저장된 source가 없는 Flow input에만 값을 제공합니다. 같은 port에 saved mapping과 case input을 동시에 보내면 validation error입니다. 결과를 Report Table에 합칠 때는 저장된 result mapping key, mapping generation, result/target field mapping과 stable case key를 고정합니다. child Run 완료 순서로 행을 결합하지 않습니다.

Search에서 Apply까지

LLM은 다음 단계로만 의미 변경을 적용합니다. Project Function은 function_*, Flow는 pxflow_*, Report는 report_* Tool family를 사용합니다.

  1. search: 명시한 Workspace·Project 범위에서 후보와 stable ref를 찾습니다.
  2. describe: exact revision의 contract, 권한, capability와 사용처를 읽습니다.
  3. validate: type·unit·cardinality와 source-safe 작성 가능성을 확인합니다.
  4. plan: normalized command, impact, diff, required approval과 precondition을 고정합니다.
  5. apply: plan digest, expected generation, idempotency key와 approval receipt를 재검사하고 전체 command를 원자적으로 적용합니다.

Flow 첫 저장도 이 순서를 벗어나지 않습니다. validate가 돌려준 canonical direct document byteDigest를 target revisionRef로, document의 flowKey를 target flowKey로 쓰고 generation 0에서 CreateFlow { document } 하나만 plan한 뒤 같은 target으로 apply합니다. 성공은 그 digest와 generation 0을 돌려주며 이미 head가 있는 flowKey는 FlowHeadAlreadyExists로 거부합니다. 별도 create Tool을 두지 않아 Flow·Report·Function 생성 감사 기록과 generation 충돌 검사가 같은 plan→apply 경로에 남습니다.

Apply 중 한 command라도 실패하면 같은 transaction의 Flow·Report revision을 만들지 않습니다. stale generation은 자동 merge하지 않고 최신 revision에 대해 validate와 plan을 다시 수행합니다. Python source가 authoring owner인 Project Function과 Flow는 source-safe diff를 먼저 보여 주고, rematerialize한 semantic contract가 plan과 같을 때만 적용합니다. Function plan은 승인된 Node·Flow·Report reference update까지 같은 atomic apply에 포함합니다.

ContextPack·result 경계와 legacy 호출

AI authoring의 ContextPack과 result는 같은 보수적 V1 budget을 사용합니다.

surfacetokensbytesitemstableRowsdepth
contextPack200000209715250010008
result200000209715250010008
  • tokens: 200000은 ordinary Project의 ContextPack을 common model context window 안에 두고 request당 prompt 조립·tokenization 비용을 제한합니다.
  • bytes: 2097152(2 MiB)는 token 추정과 무관하게 request당 memory와 serialization payload를 2 MiB로 제한합니다.
  • items: 500은 search·descriptor·resource candidate fan-out과 validator work를 제한합니다.
  • tableRows: 1000은 일반 검토용 preview를 유지하면서 row materialization, JSON encoding과 전송을 1,000행으로 제한합니다.
  • depth: 8은 nested Record/List/Table traversal, validation과 serialization 재귀 비용을 bounded하게 유지합니다.

이 상한은 durable Result capacity가 아니라 per-request memory·serialization bound입니다. ordinary Project의 한 ContextPack은 common model window 안에 유지되어 일반 작업을 위해 두 번째 왕복을 강제하지 않습니다. 초과 시에는 data.truncationtruncated, budget, limit, observed, continuation을 모두 반환하고 continuation이 다음 요청의 cursor와 opaque value를 지정합니다. Silent truncation은 partial data를 전체로 오인시켜 확신 있는 잘못된 계산 결론을 만드는 correctness failure이므로 허용하지 않습니다.

Formal V1 endpoint가 거부하는 exact legacy set은 다음 21개입니다.

kindlegacy entrystable diagnostic
fieldprojectIdPX_SCHEMA_UNKNOWN_FIELD
fieldflowIdPX_SCHEMA_UNKNOWN_FIELD
fieldnodeIdPX_SCHEMA_UNKNOWN_FIELD
fieldprojectRootPX_SCHEMA_UNKNOWN_FIELD
fieldflowPathPX_SCHEMA_UNKNOWN_FIELD
payloadraw-dotted-result-pathPX_SCHEMA_UNSUPPORTED_VERSION
toolproject.searchPX_SCHEMA_UNSUPPORTED_VERSION
tooladapter.searchPX_SCHEMA_UNSUPPORTED_VERSION
tooladapter.describePX_SCHEMA_UNSUPPORTED_VERSION
toolpxflow.searchPX_SCHEMA_UNSUPPORTED_VERSION
toolpxflow.describePX_SCHEMA_UNSUPPORTED_VERSION
toolpxflow.validatePX_SCHEMA_UNSUPPORTED_VERSION
toolpxflow.changePX_SCHEMA_UNSUPPORTED_VERSION
toolpxflow.runPX_SCHEMA_UNSUPPORTED_VERSION
toolreport.searchPX_SCHEMA_UNSUPPORTED_VERSION
toolreport.describePX_SCHEMA_UNSUPPORTED_VERSION
toolreport.changePX_SCHEMA_UNSUPPORTED_VERSION
toolreport.renderPX_SCHEMA_UNSUPPORTED_VERSION
toolrun.getPX_SCHEMA_UNSUPPORTED_VERSION
toolrun.cancelPX_SCHEMA_UNSUPPORTED_VERSION
toolresult.readPX_SCHEMA_UNSUPPORTED_VERSION

거부된 호출은 stable RFC-003 Diagnostic을 반환하며 무시, alias, translation 또는 formal endpoint 안의 fallback으로 성공처럼 보이지 않습니다. 조용히 무시한 legacy change는 모델이 일어나지 않은 변경을 사실로 삼게 합니다. 구 client는 격리 endpoint에만 routing하고 formal/legacy telemetry를 분리하며, rollback은 cohort routing만 바꿉니다. 격리 endpoint는 zero-use, support, rollback gate를 모두 통과한 뒤 제거합니다.

Native surface V1 계약

CLI subcommand는 21-Tool contract name과 identity mapping이고 alias가 없습니다. 인자는 optional --arguments <JSON object> 하나이며 생략은 {}, non-object는 usage refusal입니다. Catalog는 argv[1]의 top-level --list-operations이고 trailing argument를 거부하며 22번째 Tool이 아닙니다. Exit code는 normalized 0, failed-closed Diagnostic 1, fixed Diagnostic 없는 refusal 2, usage/dispatcher error 3, 이미 실행된 completed outcome 4로 닫힙니다. run은 순수 routing만 하므로 4run에서 나오지 않으며, mapping이 total하게 유지되도록 존재합니다. 실행된 응답은 ok:false를 담을 수 있어서 0이 아닙니다.

MCP V1은 request당 newline-delimited JSON-RPC 2.0 message 하나를 처리하는 stateless surface이며 tools/listtools/call만 인식합니다. Initialize, capability/session/progress retention과 Task-to-Run mapping은 제공하지 않습니다. Dispatch된 업무 결과는 content: [], isError: false, unmodified OperationOutcomestructuredContent를 반환하고, unreadable params와 router failure만 JSON-RPC error입니다. 공통 outcome은 normalized | completed | failedClosed | refusedWithoutFixedDiagnostic의 closed union입니다.

completed는 2026-09-03에 추가되었고 normalized와 절대 같은 뜻이 아닙니다. normalized는 실행을 주장하지 않습니다 -- 호출이 fixed catalog 안에 있고 이 operation으로 환원된다는 것만 말하며, 그것이 순수 adapter가 알 수 있는 전부입니다. completed는 호출이 실행되었고 response가 runtime이 돌려준 정확한 ResponseEnvelope라는 뜻이며, 권장 wire는 {"outcome":"completed","response":<ResponseEnvelope>}입니다. 이 arm 이전에는 crates/pxflow-runtime/src/surface_execution.rsnormalized payload를 envelope으로 덮어썼고, 그 결과 문서가 더 이상 OperationOutcome이 아니게 되어 실행된 호출과 실행되지 않은 호출을 구분할 수 없었습니다.

Fixed Diagnostic 없는 refusal reason은 다섯 구현 값으로 닫힙니다: router의 네 값 (operationOutsideFixedCatalog, targetNotAnObject, targetWithoutExplicitScope, scopeNotProjectScoped)과 invariantViolation. 마지막 값은 저장 레코드나 내부 불변식이 이 build가 요구하는 것과 다르다는 뜻이며, 호출자가 조치할 수 없으므로 business refusal이 아닙니다 -- 그래서 ResponseEnvelope 안의 Diagnostic으로 위장하지 않습니다. 함께 실리는 detail표시 전용 비규범 문자열입니다. 어떤 client도 parsing하거나 match하거나 분기하지 않으며, branch point는 reason token뿐입니다.

Supervisor socket은 이번 버전에서 바뀌지 않습니다. 완료·business refusal 요청은 예전과 바이트 동일한 4-member ResponseEnvelope로 답하고, invariantViolation refusal만 위 outcome 모양으로 답합니다. 따라서 변경은 한 socket 위의 additive union이며, 이 outcome을 모르는 기존 client는 아무 변화도 겪지 않습니다.

UI request는 consoleRequest{operationName,argumentsText}, view는 consoleView{operationName,submitState,result?|inputError?}입니다. Blank text는 {}, valid object는 dispatch, malformed/non-object는 dispatch 없는 notSubmittable입니다. 기존 세 console export는 승인된 WASM tuple을 그대로 사용합니다. Bounded-response encoder는 contract code지만 V1 surface entry point는 없고 요청은 bounded-response-entry-point-not-ratified로 거부합니다.

Performance contract의 네 멤버는 서로 다른 상태입니다. maximumArgumentBytes는 성능 수치가 아니라 supervisor frame의 프로토콜 경계에서 매 요청마다 유도합니다. Compact JSON {release,operation,arguments}의 UTF-8 바이트 수는 1,048,576 이하여야 하며 newline terminator는 frame 크기에 포함하지 않습니다. 빈 release·operation과 {}를 직렬화한 44바이트에서 argument의 2바이트를 빼면 고정 envelope 비용은 42바이트입니다. 따라서 이론상 ceiling은 1,048,576 - 42 = 1,048,534 argument 바이트이고, 실제 요청 상한은 여기서 JSON escaping이 끝난 release와 operation 문자열의 encoded content 바이트를 각각 뺀 값입니다. 두 문자열 길이가 가변이므로 하나의 universal argument 숫자를 고르는 것은 계약이 아닙니다.

measurementProfilerfc010-native-surface-v1으로 고정합니다. Released minimum-supported desktop 등급의 같은 host class를 baseline과 candidate에 사용하고, clean reboot 뒤 mains/highest-performance mode에서 build·test·browser·다른 benchmark를 겹쳐 실행하지 않습니다. Locked release build와 첫 non-empty authoring_shape_cases를 사용합니다. 한 operation은 native input 하나가 parse되어 shared dispatcher를 지나 native output 하나로 encode될 때까지이며 fixture 생성과 assertion은 제외합니다. CLI는 rendered argv의 run, UI는 rendered consoleRequestsubmit_native, MCP는 parsed Value shortcut이 아니라 rendered JSON-RPC text의 handle_message를 잽니다. Cold는 새 process와 surface를 만든 뒤 첫 call을 30회 독립 실행하고, warm은 surface마다 1,000회 warm-up 뒤 100,000 operations를 30회 독립 실행합니다. Surface 순서는 회차마다 돌리고, cold/warm p50·p95·p99와 one-in-flight warm throughput을 따로 보고합니다.

latencyPercentilesminimumThroughput의 product 값만 representative released-minimum host가 준비되어 이 profile을 실행할 때까지 deferred입니다. 기존 Ubuntu 한 대의 median은 profile을 충족하지 않는 관측이며 product budget이나 failing threshold가 아닙니다.

Specification claim resolution은 direct-flow schema와 explicit core serde rename을 spelling authority로 인정합니다. PascalCase는 같은 ASCII word sequence의 lower-camel에만 대응하며, 금지하려고 쓴 값은 forbidden-by-specification입니다. FOUND는 선언 corpus 안 exact spelling 존재만 증명합니다. Corpus는 production crates/packages/apps/scripts와 public contracts이고 specification prose와 Report_Doc scripts는 포함하지 않습니다.

LLM 출력과 실행 권위

LLM이 만든 Python이나 설명 text 자체를 실행 권위로 사용하지 않습니다. 실행 권위는 검증된 saved Flow revision, exact Report mapping, normalized command와 Runtime input입니다. Python 출력은 사람이 읽고 편집할 수 있는 authoring 표현이며 같은 contract로 다시 materialize되어야 합니다.

AI가 만든 변경에는 다음 provenance를 남깁니다.

  • model과 tool/schema version
  • base revision과 source digest
  • plan ref와 plan digest
  • 승인한 사용자 또는 정책
  • 적용 뒤 revision과 semantic digest

배포 전 conformance gate

SDK·Studio·Workbench·MCP를 함께 배포하려면 다음 검사가 모두 통과해야 합니다.

  • 공개 Python 예제를 import하고 supported authoring profile로 materialize
  • 같은 fixture를 Python → .pxflow/.pxreport → Studio → Python으로 왕복한 semantic digest 일치
  • Python type stub, JSON Schema와 MCP schema를 같은 contract version에서 생성
  • Function create/edit/branch가 고정 command를 사용하고 dependency source write를 거부
  • 사용 중 Function edit이 모든 current usage·Flow·Report impact를 반환하고 atomic reference update를 검증
  • 특정 Node 분기가 새 functionKeyReplaceNodeTarget만 바꾸며 다른 사용처를 유지
  • Function source-safe diff 후 rematerialize parity가 다르면 모든 multi-target write를 취소
  • 잘못된 type·unit·cardinality, stale generation과 권한 거부가 같은 Diagnostic code/path를 반환
  • Batch가 mapped port의 두 번째 producer와 completion-order merge를 거부
  • 이전 계획 문서가 현재 검색 index나 LLM retrieval source에서 canonical 문법으로 노출되지 않음

문서의 code block도 이 gate의 fixture입니다. npm run docs:contract는 고위험 문법 drift를 정적으로 확인하고, SDK 구현 repository의 conformance suite가 import·materialize·round-trip을 검증합니다.