CHOAM

Culture must flow

602 도큐멘테이션Documentationドキュメント Engine v1.2.0 · Editor v0.1.2 · ABI v1
릴리스 다운로드Release Downloadリリースダウンロード

602 기술 레퍼런스 · Engine v1.2.0 & Editor v0.1.2 602 Technical Reference · Engine v1.2.0 & Editor v0.1.2 602 技術リファレンス · Engine v1.2.0 & Editor v0.1.2

602 도큐멘테이션 602 Documentation 602 ドキュメント

호스트 주도 턴 스토리 런타임 602 Story Engine SDK (v1.2.0, C ABI v1)와 시각적 노드 작성·중간 장면 시뮬레이션·원격 MCP 서버를 통합한 독립 실행형 602 Story Editor (v0.1.2)의 공식 개발자 명세입니다.

Complete developer specification for the host-driven 602 Story Engine SDK (v1.2.0, C ABI v1) and the standalone 602 Story Editor (v0.1.2) with visual graph authoring, arbitrary-turn simulation, and Remote MCP.

ホスト主導のターン制ストーリーランタイム 602 Story Engine SDK (v1.2.0, C ABI v1) と、ビジュアルグラフ編集・途中ターンからのシミュレーション・リモート MCP を備えたスタンドアロン版 602 Story Editor (v0.1.2) の公式開発者リファレンスです。

바이너리 배포 패키지

릴리스 다운로드

약관 확인 후 운영체제별 아카이브(.zip)와 .sha256 체크섬을 받을 수 있습니다.

  • 602 Story Engine SDK (v1.2.0 · CHOAM Community License 1.0)
    linux-x64, windows-x64, macos-arm64, macos-x64
  • 602 Story Editor (v0.1.2 · Apache-2.0)
    linux-x64, windows-x64, macos-arm64

BINARY RELEASES

Release Download

Review the license terms and download platform .zip archives and .sha256 checksums.

  • 602 Story Engine SDK (v1.2.0 · CHOAM Community License 1.0)
    linux-x64, windows-x64, macos-arm64, macos-x64
  • 602 Story Editor (v0.1.2 · Apache-2.0)
    linux-x64, windows-x64, macos-arm64

バイナリ配布パッケージ

リリースダウンロード

規約同意のうえ、OS 別の .zip アーカイブおよび .sha256 チェックサムを入手できます。

  • 602 Story Engine SDK (v1.2.0 · CHOAM Community License 1.0)
    linux-x64, windows-x64, macos-arm64, macos-x64
  • 602 Story Editor (v0.1.2 · Apache-2.0)
    linux-x64, windows-x64, macos-arm64

01 · 개요 및 아키텍처

인터랙티브 내러티브의 선택 분기는 기하급수적(2n)으로 팽창합니다. 수백 개의 조건식과 상태 전이가 게임 클라이언트 코드 곳곳에 흩어지면 인간 작성자와 AI 에이전트 모두 전체 도달 가능성을 추적할 수 없게 됩니다. 602는 분기 흐름, 타입/참조 정적 검증, 결정론적 상태 전이를 전용 런타임으로 완전히 분리하고, 게임 엔진은 오직 렌더링·연출·UI 입력·프레임 루프만 담당하도록 설계되었습니다.

호스트 주도 실행 계약 (Host-Driven Execution Contract)

  • 숨겨진 이벤트 루프 및 백그라운드 스레드 없음: 602는 자체 틱 루프, 강제 일시정지, 백그라운드 가비지 컬렉터 스레드를 실행하지 않습니다. 모든 컴파일, 턴 진행, 상태 조회/갱신, 메모리 정리는 호스트가 명시적으로 호출한 시점에만 수행됩니다.
  • 정적 그래프 및 명목 타입 검증: 컴파일 단계(se_compile 또는 storyc check/compile)에서 미존재 턴 참조, 엔티티 상속 순환, 표현식 DAG 순환, 그룹 매개변수 isa 불일치, 타입 불일치를 실행 전에 모두 차단합니다. 컴파일이 거부되면 프로그램 핸들은 절대 발행되지 않습니다.
  • 원자적 상태 전이 및 실패 시 무변경 보장: se_session_start, se_session_advance, se_session_advance_to_yield, se_session_update가 SE_OK 이외의 상태를 반환하면 세션의 현재 단계(phase), 대기 중인 선택지 요청, 모든 상태 슬롯 값, 변경 버전(version)은 호출 직전 상태 그대로 보존됩니다.
  • 즉시 분기 복제 및 불투명 바이너리 체크포인트: se_session_clone으로 추론적 분기 탐색용 독립 세션을 즉시 복제하고, se_session_save / se_session_load로 원본 프로그램 지문과 대조 검증되는 불투명(opaque) 바이너리 바이트 버퍼 형태로 세이브 데이터를 직렬화·복원합니다.

배포 패키지 매트릭스

602는 게임 클라이언트/서버에 링크하는 경량 네이티브 SDK와, 웹 브라우저 기반 그래프 작성·시뮬레이션·에이전트 협업을 제공하는 독립 실행형 에디터 두 가지 패키지로 배포됩니다. 바이너리 아카이브는 다운로드 포털 및 공개 릴리스 저장소(CHOAM-CO/602-releases)에서 받을 수 있습니다.

패키지 버전 지원 플랫폼 아카이브 주요 구성 요소 외부 런타임 의존성
602 Story Engine SDK v1.2.0 (ABI v1) linux-x64, windows-x64, macos-arm64, macos-x64 (4개 플랫폼) 단일 C 헤더(story_engine.h), 공유 라이브러리(.so / .dll+.lib / .dylib), storyc CLI, C 예제, SHA-256 manifest.json 없음 (표준 C 런타임 / OS 기본 로더만 사용)
602 Story Editor v0.1.2 (Engine v1.2.0 내장) linux-x64 (Ubuntu 22.04), windows-x64 (Win Server 2022), macos-arm64 (macOS 15 Apple Silicon) — 3개 플랫폼 story-editor 서버·계정 CLI, agent/story-agent, 웹 프론트엔드(static/), 동봉 Engine v1.2.0 헤더·공유 라이브러리, OpenAPI/MCP 스키마 Python / Node.js 사전 설치 불필요 (운영상 HTTPS 리버스 프록시 필요)

패키지 차이 참고: 독립 배포되는 Story Engine SDK는 Intel 기반 macOS(macos-x64)와 storyc CLI를 포함한 4개 플랫폼을 지원합니다. 반면 Story Editor v0.1.2는 linux-x64, windows-x64, macos-arm64 3개 플랫폼으로만 배포되며, 내장 네이티브 엔진 라이브러리를 직접 호출하므로 storyc 실행 파일은 포함하지 않습니다.

01 · Overview & Architecture

Branching narratives grow exponentially (2n). Once hundreds of story predicates and state mutations are scattered across gameplay scripts, neither human writers nor AI agents can reliably audit reachability or state invariants. 602 isolates story graph topology, compile-time type/reference verification, and deterministic turn transitions inside a dedicated native runtime—leaving your game engine solely in charge of rendering, audio, UI input, and frame pacing.

Host-Driven Execution Contract

  • Zero Hidden Event Pumps or Background Threads: 602 never spawns a background GC thread, never hooks your frame loop, and never pauses gameplay on its own. Compilation, turn stepping, state reads/updates, and bounded reclamation run strictly when your host invokes them.
  • Compile-Time Graph & Nominal Type Verification: se_compile (and storyc check / compile) statically rejects broken turn links, entity inheritance cycles, expression DAG cycles, group isa contract violations, and nominal type mismatches before runtime. Rejected definitions never publish a program handle.
  • Transactional Transitions & Failure Preservation: If se_session_start, se_session_advance, se_session_advance_to_yield, or se_session_update returns any status other than SE_OK, the session’s execution phase, pending choice request, state slot values, and mutation version remain untouched.
  • Speculative Cloning & Opaque Binary Checkpoints: Clone live sessions with se_session_clone for speculative branch previews or rollback, and persist/restore runtime state via se_session_save / se_session_load as opaque binary byte buffers verified by the engine against the originating program.

Distribution Package Matrix

602 ships as two complementary binary distributions: a lightweight embeddable C ABI SDK for game clients/servers, and a self-contained visual authoring, simulation, and MCP server package. Archives are available from the Download Portal and public release repository (CHOAM-CO/602-releases).

Package Version Supported Platform Archives Included Components External Runtime Dependencies
602 Story Engine SDK v1.2.0 (ABI v1) linux-x64, windows-x64, macos-arm64, macos-x64 (4 platforms) Single C header (story_engine.h), shared library (.so / .dll+.lib / .dylib), storyc CLI, C examples, SHA-256 manifest.json None (OS loader & standard runtime libraries only)
602 Story Editor v0.1.2 (bundles Engine v1.2.0) linux-x64 (Ubuntu 22.04), windows-x64 (Win Server 2022), macos-arm64 (macOS 15 Apple Silicon) — 3 platforms story-editor server/account CLI, agent/story-agent, desktop web UI (static/), bundled Engine v1.2.0 header & shared library, OpenAPI/MCP schemas No pre-installed Python or Node.js required (requires an HTTPS reverse proxy in deployment)

Packaging Note: The standalone Story Engine SDK ships for 4 platforms (including macos-x64) and includes the storyc CLI binary. Story Editor v0.1.2 ships for 3 platforms (linux-x64, windows-x64, macos-arm64) and drives its bundled native Story Engine library directly rather than bundling storyc.

01 · 概要とアーキテクチャ

インタラクティブ作品の選択肢分岐は指数関数的(2n)に増大します。数百の条件分岐や状態更新がゲーム本体のコードに混在すると、人間のシナリオ担当者も AI エージェントも到達可能性や状態の整合性を把握しきれなくなります。602 は分岐フロー・型/参照の静的検証・決定論的な状態遷移を専用ネイティブランタイムへ分離し、ホスト側のゲームエンジンが描画・演出・UI 入力・フレーム制御のみに専念できる設計を採っています。

ホスト主導の実行契約(Host-Driven Execution Contract)

  • 隠れたイベントループやバックグラウンドスレッドなし: 602 は独自の更新ループ、自動ポーズ、バックグラウンド GC スレッドを一切持ちません。コンパイル・ターン進行・状態の読み書き・メモリ回収はすべてホストが明示的に呼び出したタイミングでのみ実行されます。
  • 静的グラフ検証と公称型システム: se_compile(および storyc check / compile)が未定義ターンの参照、エンティティ継承の循環、式 DAG の循環、グループ isa 制約違反、型の不一致を実行前に検出します。検証に失敗した定義からプログラムハンドルが発行されることはありません。
  • アトミックな遷移と失敗時の状態保持: se_session_start、se_session_advance、se_session_advance_to_yield、se_session_update が SE_OK 以外を返した場合、セッションの phase、保留中の選択要求、すべての状態値、変更カウンタ(version)は呼び出し前のまま完全に保持されます。
  • セッション複製と不透明バイナリチェックポイント: se_session_clone により分岐の先読みやロールバック用の独立セッションを即座に複製でき、se_session_save / se_session_load により元のプログラムと照合検証される不透明(opaque)なバイナリバイトバッファとして状態を保存・復元できます。

配布パッケージ一覧

602 はゲームクライアント/サーバーへ組み込む軽量ネイティブ SDK と、ブラウザベースのグラフ編集・シミュレーション・リモート MCP を備えたスタンドアロンエディタの 2 形態で提供されます。各アーカイブは ダウンロードページ および公開リリースリポジトリ(CHOAM-CO/602-releases)から入手できます。

パッケージ バージョン 対応プラットフォーム 主な同梱物 外部ランタイム依存
602 Story Engine SDK v1.2.0 (ABI v1) linux-x64, windows-x64, macos-arm64, macos-x64(4 プラットフォーム) 単一 C ヘッダ(story_engine.h)、共有ライブラリ(.so / .dll+.lib / .dylib)、storyc CLI、C サンプル、SHA-256 manifest.json なし(OS 標準ローダ・C ランタイムのみ)
602 Story Editor v0.1.2(Engine v1.2.0 同梱) linux-x64(Ubuntu 22.04)、windows-x64(Win Server 2022)、macos-arm64(macOS 15 Apple Silicon)— 3 プラットフォーム story-editor サーバー/アカウント CLI、agent/story-agent、Web UI(static/)、同梱 Engine v1.2.0 ヘッダ・共有ライブラリ、OpenAPI/MCP スキーマ Python / Node.js の事前導入不要(運用時は前段に HTTPS リバースプロキシが必要)

パッケージ構成の違い: 単体配布の Story Engine SDK は Intel macOS(macos-x64)および storyc CLI を含む 4 プラットフォーム向けです。一方、Story Editor v0.1.2 は linux-x64・windows-x64・macos-arm64 の 3 プラットフォーム配布であり、内蔵ネイティブライブラリを直接呼び出すため storyc 実行ファイルは含まれません。

02 · 설치 및 링크 가이드

Story Engine SDK v1.2.0 아카이브(story-engine-v1.2.0-<platform>.zip)는 압축 해제 즉시 C11 이상 또는 C++11 이상의 호스트 프로젝트에서 링크할 수 있는 단일 헤더와 공유 라이브러리로 구성됩니다.

SDK 아카이브 디렉터리 레이아웃

story-engine-v1.2.0-<platform>/
├── bin/
│   ├── storyc                  # CLI 검증·컴파일·실행 도구 (macOS / Linux)
│   ├── storyc.exe              # CLI 검증·컴파일·실행 도구 (Windows x64)
│   └── story_engine.dll        # 런타임 공유 라이브러리 DLL (Windows x64)
├── include/
│   └── story_engine/
│       └── story_engine.h      # 공개 C ABI 단일 헤더 (ABI v1, 27개 함수)
├── lib/
│   ├── libstory_engine.dylib   # 공유 라이브러리 (macOS arm64 / macos-x64)
│   ├── libstory_engine.so      # 공유 라이브러리 (Linux x64)
│   └── story_engine_import.lib # MSVC / clang-cl 임포트 라이브러리 (Windows x64)
├── docs/
│   └── examples/               # quickstart.c, run_quickstart.py, story.json, entry.json
├── licenses/
│   └── rapidjson.txt           # 서드파티 오픈소스 라이선스 고지
└── manifest.json               # 배포 파일별 SHA-256 체크섬 매니페스트

플랫폼별 직접 컴파일 명령 (C11)

압축 해제한 아카이브 루트 디렉터리에서 호스트 바이너리를 직접 빌드하는 명령입니다. Windows에서 story_engine.dll을 동적 링크할 때는 반드시 STORY_ENGINE_USE_SHARED 전처리기 매크로를 정의해 SE_API가 __declspec(dllimport)로 확장되도록 지정해야 합니다.

# macOS (macos-arm64 / macos-x64 — Apple Clang)
clang -std=c11 -Wall -Wextra -Werror -pedantic \
  -I./include docs/examples/quickstart.c \
  -L./lib -lstory_engine -Wl,-rpath,"$(pwd)/lib" \
  -o /tmp/quickstart

# Linux (linux-x64 — GCC / Clang)
cc -std=c11 -Wall -Wextra -Werror -pedantic \
  -I./include docs/examples/quickstart.c \
  -L./lib -lstory_engine -Wl,-rpath,"$(pwd)/lib" \
  -o /tmp/quickstart

# Windows (windows-x64 — MSVC cl.exe, x64 Native Tools Prompt)
cl /std:c11 /W4 /DSTORY_ENGINE_USE_SHARED /I"include" ^
  docs\examples\quickstart.c ^
  /link /LIBPATH:"lib" story_engine_import.lib /OUT:quickstart.exe

# Windows (windows-x64 — LLVM clang-cl.exe)
clang-cl /std:c11 /W4 /DSTORY_ENGINE_USE_SHARED /I"include" ^
  docs\examples\quickstart.c lib\story_engine_import.lib /Fe:quickstart.exe

CMake SHARED IMPORTED 타깃 연동

-DSTORY_ENGINE_PACKAGE=/abs/path/to/story-engine-v1.2.0-<platform>를 받아 플랫폼별 공유 라이브러리와 헤더, Windows STORY_ENGINE_USE_SHARED 매크로를 깔끔하게 캡슐화하는 CMakeLists.txt 구성입니다.

cmake_minimum_required(VERSION 3.18)
project(game_host LANGUAGES C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

if(NOT STORY_ENGINE_PACKAGE)
  message(FATAL_ERROR "Pass -DSTORY_ENGINE_PACKAGE=/path/to/story-engine-v1.2.0-<platform>")
endif()

add_library(story_engine SHARED IMPORTED GLOBAL)
set_target_properties(story_engine PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "${STORY_ENGINE_PACKAGE}/include"
)

if(WIN32)
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/bin/story_engine.dll"
    IMPORTED_IMPLIB   "${STORY_ENGINE_PACKAGE}/lib/story_engine_import.lib"
    INTERFACE_COMPILE_DEFINITIONS "STORY_ENGINE_USE_SHARED"
  )
elseif(APPLE)
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/lib/libstory_engine.dylib"
  )
else()
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/lib/libstory_engine.so"
  )
endif()

add_executable(game_host src/main.c)
target_link_libraries(game_host PRIVATE story_engine)

미서명 바이너리 실행 유의사항: 현재 배포 바이너리에는 Apple 공증(notarization) 및 Windows Authenticode 상용 코드 서명이 적용되어 있지 않습니다. 반드시 공식 출처(choam.studio 또는 CHOAM-CO/602-releases) 및 SHA-256 해시를 먼저 확인한 뒤 아래 절차를 적용하십시오.

  • macOS Gatekeeper 격리 해제: 압축 해제 폴더에서 xattr -dr com.apple.quarantine ./story-engine-v1.2.0-macos-arm64 (또는 story-editor-v0.1.2-macos-arm64) 실행
  • Windows SmartScreen 및 DLL 경로: 출처 확인 후 추가 정보 → 실행 선택. 호스트 실행 시 bin\story_engine.dll을 실행 파일 옆에 복사하거나 PATH에 bin 폴더를 추가하십시오.
  • Linux 실행 권한: 파일 권한이 유실된 경우 chmod +x ./bin/storyc (에디터는 chmod +x ./story-editor ./agent/story-agent) 실행

02 · Installation & Linking

Each Story Engine SDK v1.2.0 archive (story-engine-v1.2.0-<platform>.zip) extracts into a self-contained layout ready to link into any C11+ or C++11+ host project.

SDK Archive Directory Layout

story-engine-v1.2.0-<platform>/
├── bin/
│   ├── storyc                  # CLI validator, compiler & runner (macOS / Linux)
│   ├── storyc.exe              # CLI validator, compiler & runner (Windows x64)
│   └── story_engine.dll        # Runtime shared library DLL (Windows x64)
├── include/
│   └── story_engine/
│       └── story_engine.h      # Single public C ABI header (ABI v1, 27 functions)
├── lib/
│   ├── libstory_engine.dylib   # Shared library (macOS arm64 / macos-x64)
│   ├── libstory_engine.so      # Shared library (Linux x64)
│   └── story_engine_import.lib # MSVC / clang-cl DLL import library (Windows x64)
├── docs/
│   └── examples/               # quickstart.c, run_quickstart.py, story.json, entry.json
├── licenses/
│   └── rapidjson.txt           # Third-party open-source license notice
└── manifest.json               # Per-file SHA-256 checksum manifest

Direct Compiler Commands (C11)

Run these commands from the extracted archive root. When linking dynamically against story_engine.dll on Windows, define STORY_ENGINE_USE_SHARED so SE_API expands to __declspec(dllimport).

# macOS (macos-arm64 / macos-x64 — Apple Clang)
clang -std=c11 -Wall -Wextra -Werror -pedantic \
  -I./include docs/examples/quickstart.c \
  -L./lib -lstory_engine -Wl,-rpath,"$(pwd)/lib" \
  -o /tmp/quickstart

# Linux (linux-x64 — GCC / Clang)
cc -std=c11 -Wall -Wextra -Werror -pedantic \
  -I./include docs/examples/quickstart.c \
  -L./lib -lstory_engine -Wl,-rpath,"$(pwd)/lib" \
  -o /tmp/quickstart

# Windows (windows-x64 — MSVC cl.exe, x64 Native Tools Prompt)
cl /std:c11 /W4 /DSTORY_ENGINE_USE_SHARED /I"include" ^
  docs\examples\quickstart.c ^
  /link /LIBPATH:"lib" story_engine_import.lib /OUT:quickstart.exe

# Windows (windows-x64 — LLVM clang-cl.exe)
clang-cl /std:c11 /W4 /DSTORY_ENGINE_USE_SHARED /I"include" ^
  docs\examples\quickstart.c lib\story_engine_import.lib /Fe:quickstart.exe

CMake SHARED IMPORTED Target

Pass -DSTORY_ENGINE_PACKAGE=/abs/path/to/story-engine-v1.2.0-<platform> to encapsulate include paths, shared library locations, and Windows import definitions cleanly:

cmake_minimum_required(VERSION 3.18)
project(game_host LANGUAGES C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

if(NOT STORY_ENGINE_PACKAGE)
  message(FATAL_ERROR "Pass -DSTORY_ENGINE_PACKAGE=/path/to/story-engine-v1.2.0-<platform>")
endif()

add_library(story_engine SHARED IMPORTED GLOBAL)
set_target_properties(story_engine PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "${STORY_ENGINE_PACKAGE}/include"
)

if(WIN32)
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/bin/story_engine.dll"
    IMPORTED_IMPLIB   "${STORY_ENGINE_PACKAGE}/lib/story_engine_import.lib"
    INTERFACE_COMPILE_DEFINITIONS "STORY_ENGINE_USE_SHARED"
  )
elseif(APPLE)
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/lib/libstory_engine.dylib"
  )
else()
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/lib/libstory_engine.so"
  )
endif()

add_executable(game_host src/main.c)
target_link_libraries(game_host PRIVATE story_engine)

Unsigned Binary Handling: Current release archives are not Apple-notarized (ad-hoc signed on macOS) and do not carry Windows Authenticode certificates. Always verify the archive origin (choam.studio or CHOAM-CO/602-releases) and SHA-256 checksum first:

  • macOS Quarantine Removal: Run xattr -dr com.apple.quarantine ./story-engine-v1.2.0-macos-arm64 (or ./story-editor-v0.1.2-macos-arm64) on the extracted directory.
  • Windows SmartScreen & DLL Search Path: Confirm the file origin if SmartScreen prompts (More info → Run anyway), and copy bin\story_engine.dll next to your executable or add bin to PATH.
  • Linux Executable Permissions: Ensure execute bits are set via chmod +x ./bin/storyc (or chmod +x ./story-editor ./agent/story-agent for the Editor archive).

02 · インストールとリンク

Story Engine SDK v1.2.0 の配布アーカイブ(story-engine-v1.2.0-<platform>.zip)を展開すると、C11 以上または C++11 以上のホストプロジェクトからそのままリンクできる単一ヘッダと共有ライブラリが揃っています。

SDK アーカイブのディレクトリ構成

story-engine-v1.2.0-<platform>/
├── bin/
│   ├── storyc                  # CLI 検証・コンパイル・実行ツール (macOS / Linux)
│   ├── storyc.exe              # CLI 検証・コンパイル・実行ツール (Windows x64)
│   └── story_engine.dll        # ランタイム共有ライブラリ DLL (Windows x64)
├── include/
│   └── story_engine/
│       └── story_engine.h      # 公開 C ABI 単一ヘッダ (ABI v1, 全27関数)
├── lib/
│   ├── libstory_engine.dylib   # 共有ライブラリ (macOS arm64 / macos-x64)
│   ├── libstory_engine.so      # 共有ライブラリ (Linux x64)
│   └── story_engine_import.lib # MSVC / clang-cl 用インポートライブラリ (Windows x64)
├── docs/
│   └── examples/               # quickstart.c, run_quickstart.py, story.json, entry.json
├── licenses/
│   └── rapidjson.txt           # サードパーティ OSS ライセンス表記
└── manifest.json               # ファイル別 SHA-256 チェックサムマニフェスト

コンパイラ直接呼び出しによるビルド(C11)

展開したアーカイブのルートディレクトリからビルドする例です。Windows で story_engine.dll を動的リンクする際は、SE_API を __declspec(dllimport) に展開するため STORY_ENGINE_USE_SHARED マクロを定義してください。

# macOS (macos-arm64 / macos-x64 — Apple Clang)
clang -std=c11 -Wall -Wextra -Werror -pedantic \
  -I./include docs/examples/quickstart.c \
  -L./lib -lstory_engine -Wl,-rpath,"$(pwd)/lib" \
  -o /tmp/quickstart

# Linux (linux-x64 — GCC / Clang)
cc -std=c11 -Wall -Wextra -Werror -pedantic \
  -I./include docs/examples/quickstart.c \
  -L./lib -lstory_engine -Wl,-rpath,"$(pwd)/lib" \
  -o /tmp/quickstart

# Windows (windows-x64 — MSVC cl.exe, x64 Native Tools Prompt)
cl /std:c11 /W4 /DSTORY_ENGINE_USE_SHARED /I"include" ^
  docs\examples\quickstart.c ^
  /link /LIBPATH:"lib" story_engine_import.lib /OUT:quickstart.exe

# Windows (windows-x64 — LLVM clang-cl.exe)
clang-cl /std:c11 /W4 /DSTORY_ENGINE_USE_SHARED /I"include" ^
  docs\examples\quickstart.c lib\story_engine_import.lib /Fe:quickstart.exe

CMake SHARED IMPORTED ターゲット設定

-DSTORY_ENGINE_PACKAGE=/abs/path/to/story-engine-v1.2.0-<platform> を渡すことで、各 OS の共有ライブラリと STORY_ENGINE_USE_SHARED 定義を一元管理できます。

cmake_minimum_required(VERSION 3.18)
project(game_host LANGUAGES C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

if(NOT STORY_ENGINE_PACKAGE)
  message(FATAL_ERROR "Pass -DSTORY_ENGINE_PACKAGE=/path/to/story-engine-v1.2.0-<platform>")
endif()

add_library(story_engine SHARED IMPORTED GLOBAL)
set_target_properties(story_engine PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "${STORY_ENGINE_PACKAGE}/include"
)

if(WIN32)
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/bin/story_engine.dll"
    IMPORTED_IMPLIB   "${STORY_ENGINE_PACKAGE}/lib/story_engine_import.lib"
    INTERFACE_COMPILE_DEFINITIONS "STORY_ENGINE_USE_SHARED"
  )
elseif(APPLE)
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/lib/libstory_engine.dylib"
  )
else()
  set_target_properties(story_engine PROPERTIES
    IMPORTED_LOCATION "${STORY_ENGINE_PACKAGE}/lib/libstory_engine.so"
  )
endif()

add_executable(game_host src/main.c)
target_link_libraries(game_host PRIVATE story_engine)

未署名バイナリの取り扱いについて: 現在の配布バイナリには Apple 公証(notarization)および Windows Authenticode 商用署名が適用されていません(macOS は ad-hoc 署名)。必ず公式配布元(choam.studio または CHOAM-CO/602-releases)および SHA-256 ハッシュを確認した上で以下の手順を行ってください。

  • macOS 隔離属性の解除: 展開先に対して xattr -dr com.apple.quarantine ./story-engine-v1.2.0-macos-arm64(Editor は ./story-editor-v0.1.2-macos-arm64)を実行します。
  • Windows SmartScreen と DLL 配置: 配布元を確認後「詳細情報 → 実行」を選択し、実行時は bin\story_engine.dll を実行ファイルと同じフォルダに配置するか bin を PATH に追加してください。
  • Linux 実行権限: 必要に応じて chmod +x ./bin/storyc(Editor は chmod +x ./story-editor ./agent/story-agent)を付与します。

03 · 빠른 시작 (Quickstart)

아래 예제는 story-engine/turn-source/1 포맷으로 상점 통행증 등록 시나리오를 정의하고, storyc CLI로 검증·컴파일한 뒤 C11 호스트 프로그램에서 실행하는 전체 과정을 보여줍니다.

1. 스토리 정의 (story.json) 및 진입점 (entry.json)

{
  "format": "story-engine/turn-source/1",
  "name": "Guild Pass Quickstart",
  "types": [
    { "id": "Coins", "value_type": { "kind": "int" } }
  ],
  "entities": [
    { "id": "traveler", "name": "Traveler" },
    { "id": "clerk", "name": "Guild Clerk" }
  ],
  "states": [
    {
      "id": "coins",
      "owner": "traveler",
      "declared_type": "Coins",
      "initial": { "kind": "int", "value": "50" }
    }
  ],
  "expressions": [
    { "id": "coins_val", "op": "state", "state": "coins" },
    { "id": "fee", "op": "literal", "literal": { "kind": "int", "value": "20" } },
    { "id": "can_afford", "op": "greater_equal", "arguments": ["coins_val", "fee"] }
  ],
  "snapshots": [
    { "id": "opening_ledger", "states": ["coins"] }
  ],
  "turns": [
    {
      "id": "record_opening",
      "kind": "capture",
      "snapshot": "opening_ledger",
      "next": "greet"
    },
    {
      "id": "greet",
      "kind": "dialogue",
      "actor": "clerk",
      "text": "Register a guild pass for 20 coins?",
      "next": "decide"
    },
    {
      "id": "decide",
      "kind": "choice",
      "text": "Select your response:",
      "choices": [
        { "id": "pay", "text": "Pay 20 coins", "when": "can_afford", "next": "charge" },
        { "id": "leave", "text": "Decline", "next": "done" }
      ]
    },
    {
      "id": "charge",
      "kind": "update",
      "updates": [
        { "target": "coins", "op": "subtract", "expr": "fee" }
      ],
      "next": "done"
    },
    { "id": "done", "kind": "end" }
  ]
}

시작 턴을 지정하는 entry.json 파일입니다:

{ "turn": "record_opening" }

2. storyc CLI로 검증, 컴파일 및 대화형 실행

# 1) 정적 검증 — 오류·경고가 없으면 stdout에 [] 출력 후 종료 코드 0
./bin/storyc check story.json

# 2) 불투명(opaque) 바이너리 아티팩트로 컴파일
./bin/storyc compile story.json /tmp/guild.story

# 3) 시드 42로 터미널에서 대화형 시뮬레이션 실행
./bin/storyc run /tmp/guild.story entry.json 42

3. 최소 C11 호스트 통합 코드 (host_quickstart.c)

se_session_advance_to_yield를 사용하면 내부 비가시 턴(capture, update)을 예산(max_steps) 범위 안에서 원자적으로 통과하고 첫 외부 노출 턴(dialogue, action, choice, end)에서 제어를 돌려받습니다.

#include "story_engine/story_engine.h"
#include <stdio.h>

static void print_and_release(se_buffer *buf) {
    if (buf->data) {
        fwrite(buf->data, 1, buf->size, stdout);
        fputc('\n', stdout);
    }
    se_buffer_release(buf);
}

int main(void) {
    if (se_abi_version() != 1u) return 1;

    const char *source_json =
        "{\"format\":\"story-engine/turn-source/1\","
        "\"types\":[{\"id\":\"Coins\",\"value_type\":\"int\"}],"
        "\"entities\":[{\"id\":\"clerk\"}],"
        "\"states\":[{\"id\":\"coins\",\"owner\":\"clerk\",\"declared_type\":\"Coins\",\"initial\":50}],"
        "\"turns\":["
        "{\"id\":\"open\",\"kind\":\"update\",\"updates\":[{\"target\":\"coins\",\"op\":\"subtract\",\"value\":10}],\"next\":\"greet\"},"
        "{\"id\":\"greet\",\"kind\":\"dialogue\",\"actor\":\"clerk\",\"text\":\"Pass issued.\",\"next\":\"done\"},"
        "{\"id\":\"done\",\"kind\":\"end\"}"
        "]}";

    se_program program = 0;
    se_session session = 0;
    se_buffer out = {0};
    int rc = 1;

    if (se_compile(source_json, &program, &out) != SE_OK) {
        print_and_release(&out);
        goto cleanup;
    }
    se_buffer_release(&out);

    if (se_session_create(program, 42ULL, &session, &out) != SE_OK) goto cleanup;
    se_buffer_release(&out);

    if (se_session_start(session, "{\"turn\":\"open\"}", &out) != SE_OK) goto cleanup;
    se_buffer_release(&out);

    /* 내부 update("open")를 거친 뒤 첫 가시 턴("greet")과 누적 changes를 반환 */
    if (se_session_advance_to_yield(session, NULL, 16, &out) != SE_OK) goto cleanup;
    print_and_release(&out);

    /* 종료 턴("done") 실행 -> phase == "ended" */
    if (se_session_advance_to_yield(session, NULL, 16, &out) != SE_OK) goto cleanup;
    print_and_release(&out);
    rc = 0;

cleanup:
    se_buffer_release(&out);
    if (session != 0) se_session_release(session);
    if (program != 0) se_program_release(program);
    return rc;
}

03 · Quickstart

This walkthrough defines a compact guild-pass scenario in story-engine/turn-source/1 JSON, validates and compiles it with storyc, and drives it from a minimal C11 host program.

1. Author story.json & entry.json

{
  "format": "story-engine/turn-source/1",
  "name": "Guild Pass Quickstart",
  "types": [
    { "id": "Coins", "value_type": { "kind": "int" } }
  ],
  "entities": [
    { "id": "traveler", "name": "Traveler" },
    { "id": "clerk", "name": "Guild Clerk" }
  ],
  "states": [
    {
      "id": "coins",
      "owner": "traveler",
      "declared_type": "Coins",
      "initial": { "kind": "int", "value": "50" }
    }
  ],
  "expressions": [
    { "id": "coins_val", "op": "state", "state": "coins" },
    { "id": "fee", "op": "literal", "literal": { "kind": "int", "value": "20" } },
    { "id": "can_afford", "op": "greater_equal", "arguments": ["coins_val", "fee"] }
  ],
  "snapshots": [
    { "id": "opening_ledger", "states": ["coins"] }
  ],
  "turns": [
    {
      "id": "record_opening",
      "kind": "capture",
      "snapshot": "opening_ledger",
      "next": "greet"
    },
    {
      "id": "greet",
      "kind": "dialogue",
      "actor": "clerk",
      "text": "Register a guild pass for 20 coins?",
      "next": "decide"
    },
    {
      "id": "decide",
      "kind": "choice",
      "text": "Select your response:",
      "choices": [
        { "id": "pay", "text": "Pay 20 coins", "when": "can_afford", "next": "charge" },
        { "id": "leave", "text": "Decline", "next": "done" }
      ]
    },
    {
      "id": "charge",
      "kind": "update",
      "updates": [
        { "target": "coins", "op": "subtract", "expr": "fee" }
      ],
      "next": "done"
    },
    { "id": "done", "kind": "end" }
  ]
}

Create entry.json to select the entry turn:

{ "turn": "record_opening" }

2. Check, Compile & Run via storyc

# 1) Validate source JSON — prints [] to stdout and exits 0 when clean
./bin/storyc check story.json

# 2) Compile into an opaque binary story artifact
./bin/storyc compile story.json /tmp/guild.story

# 3) Run interactively from stdin/stdout with deterministic RNG seed 42
./bin/storyc run /tmp/guild.story entry.json 42

3. Minimal C11 Host Program (host_quickstart.c)

Calling se_session_advance_to_yield executes internal non-yielding turns (update, capture) atomically within max_steps and stops at the first externally observable turn (dialogue, action, choice, end) or open group exit.

#include "story_engine/story_engine.h"
#include <stdio.h>

static void print_and_release(se_buffer *buf) {
    if (buf->data) {
        fwrite(buf->data, 1, buf->size, stdout);
        fputc('\n', stdout);
    }
    se_buffer_release(buf);
}

int main(void) {
    if (se_abi_version() != 1u) return 1;

    const char *source_json =
        "{\"format\":\"story-engine/turn-source/1\","
        "\"types\":[{\"id\":\"Coins\",\"value_type\":\"int\"}],"
        "\"entities\":[{\"id\":\"clerk\"}],"
        "\"states\":[{\"id\":\"coins\",\"owner\":\"clerk\",\"declared_type\":\"Coins\",\"initial\":50}],"
        "\"turns\":["
        "{\"id\":\"open\",\"kind\":\"update\",\"updates\":[{\"target\":\"coins\",\"op\":\"subtract\",\"value\":10}],\"next\":\"greet\"},"
        "{\"id\":\"greet\",\"kind\":\"dialogue\",\"actor\":\"clerk\",\"text\":\"Pass issued.\",\"next\":\"done\"},"
        "{\"id\":\"done\",\"kind\":\"end\"}"
        "]}";

    se_program program = 0;
    se_session session = 0;
    se_buffer out = {0};
    int rc = 1;

    if (se_compile(source_json, &program, &out) != SE_OK) {
        print_and_release(&out);
        goto cleanup;
    }
    se_buffer_release(&out);

    if (se_session_create(program, 42ULL, &session, &out) != SE_OK) goto cleanup;
    se_buffer_release(&out);

    if (se_session_start(session, "{\"turn\":\"open\"}", &out) != SE_OK) goto cleanup;
    se_buffer_release(&out);

    /* Executes internal update ("open") and yields at observable dialogue ("greet") */
    if (se_session_advance_to_yield(session, NULL, 16, &out) != SE_OK) goto cleanup;
    print_and_release(&out);

    /* Advances into "done" -> phase becomes "ended" */
    if (se_session_advance_to_yield(session, NULL, 16, &out) != SE_OK) goto cleanup;
    print_and_release(&out);
    rc = 0;

cleanup:
    se_buffer_release(&out);
    if (session != 0) se_session_release(session);
    if (program != 0) se_program_release(program);
    return rc;
}

03 · クイックスタート

story-engine/turn-source/1 形式でギルド通行証シナリオを記述し、storyc CLI で検証・コンパイルした上で、最小構成の C11 ホストプログラムから駆動する手順を示します。

1. ストーリー定義(story.json)と開始設定(entry.json)

{
  "format": "story-engine/turn-source/1",
  "name": "Guild Pass Quickstart",
  "types": [
    { "id": "Coins", "value_type": { "kind": "int" } }
  ],
  "entities": [
    { "id": "traveler", "name": "Traveler" },
    { "id": "clerk", "name": "Guild Clerk" }
  ],
  "states": [
    {
      "id": "coins",
      "owner": "traveler",
      "declared_type": "Coins",
      "initial": { "kind": "int", "value": "50" }
    }
  ],
  "expressions": [
    { "id": "coins_val", "op": "state", "state": "coins" },
    { "id": "fee", "op": "literal", "literal": { "kind": "int", "value": "20" } },
    { "id": "can_afford", "op": "greater_equal", "arguments": ["coins_val", "fee"] }
  ],
  "snapshots": [
    { "id": "opening_ledger", "states": ["coins"] }
  ],
  "turns": [
    {
      "id": "record_opening",
      "kind": "capture",
      "snapshot": "opening_ledger",
      "next": "greet"
    },
    {
      "id": "greet",
      "kind": "dialogue",
      "actor": "clerk",
      "text": "Register a guild pass for 20 coins?",
      "next": "decide"
    },
    {
      "id": "decide",
      "kind": "choice",
      "text": "Select your response:",
      "choices": [
        { "id": "pay", "text": "Pay 20 coins", "when": "can_afford", "next": "charge" },
        { "id": "leave", "text": "Decline", "next": "done" }
      ]
    },
    {
      "id": "charge",
      "kind": "update",
      "updates": [
        { "target": "coins", "op": "subtract", "expr": "fee" }
      ],
      "next": "done"
    },
    { "id": "done", "kind": "end" }
  ]
}

開始ターンを指定する entry.json を作成します:

{ "turn": "record_opening" }

2. storyc CLI による検証・コンパイル・対話実行

# 1) 静的検証 — エラー・警告がなければ stdout に [] を出力して終了コード 0
./bin/storyc check story.json

# 2) 不透明(opaque)バイナリアーティファクトへコンパイル
./bin/storyc compile story.json /tmp/guild.story

# 3) 乱数シード 42 で標準入出力から対話実行
./bin/storyc run /tmp/guild.story entry.json 42

3. 最小構成の C11 ホストプログラム(host_quickstart.c)

se_session_advance_to_yield を呼び出すと、内部ターン(update・capture)をステップ予算(max_steps)内でアトミックに連続実行し、最初の外部可視ターン(dialogue・action・choice・end)またはグループ出口で制御を返します。

#include "story_engine/story_engine.h"
#include <stdio.h>

static void print_and_release(se_buffer *buf) {
    if (buf->data) {
        fwrite(buf->data, 1, buf->size, stdout);
        fputc('\n', stdout);
    }
    se_buffer_release(buf);
}

int main(void) {
    if (se_abi_version() != 1u) return 1;

    const char *source_json =
        "{\"format\":\"story-engine/turn-source/1\","
        "\"types\":[{\"id\":\"Coins\",\"value_type\":\"int\"}],"
        "\"entities\":[{\"id\":\"clerk\"}],"
        "\"states\":[{\"id\":\"coins\",\"owner\":\"clerk\",\"declared_type\":\"Coins\",\"initial\":50}],"
        "\"turns\":["
        "{\"id\":\"open\",\"kind\":\"update\",\"updates\":[{\"target\":\"coins\",\"op\":\"subtract\",\"value\":10}],\"next\":\"greet\"},"
        "{\"id\":\"greet\",\"kind\":\"dialogue\",\"actor\":\"clerk\",\"text\":\"Pass issued.\",\"next\":\"done\"},"
        "{\"id\":\"done\",\"kind\":\"end\"}"
        "]}";

    se_program program = 0;
    se_session session = 0;
    se_buffer out = {0};
    int rc = 1;

    if (se_compile(source_json, &program, &out) != SE_OK) {
        print_and_release(&out);
        goto cleanup;
    }
    se_buffer_release(&out);

    if (se_session_create(program, 42ULL, &session, &out) != SE_OK) goto cleanup;
    se_buffer_release(&out);

    if (se_session_start(session, "{\"turn\":\"open\"}", &out) != SE_OK) goto cleanup;
    se_buffer_release(&out);

    /* 内部 update ("open") を通過し、可視ターン ("greet") と累積 changes を取得 */
    if (se_session_advance_to_yield(session, NULL, 16, &out) != SE_OK) goto cleanup;
    print_and_release(&out);

    /* 終了ターン ("done") を実行 -> phase == "ended" */
    if (se_session_advance_to_yield(session, NULL, 16, &out) != SE_OK) goto cleanup;
    print_and_release(&out);
    rc = 0;

cleanup:
    se_buffer_release(&out);
    if (session != 0) se_session_release(session);
    if (program != 0) se_program_release(program);
    return rc;
}

04 · 스토리 작성 포맷 명세 (story-engine/turn-source/1)

se_compile과 storyc는 엄격한 UTF-8 JSON 객체 포맷인 story-engine/turn-source/1을 입력으로 받습니다. 미정의 필드, 중복 객체 키, 잘못된 UTF-8 바이트 시퀀스, \u0000(NUL)을 포함한 식별자는 즉시 거부되며, 모든 선언 객체·선택지·명시적 경로의 id는 스토리 전체에서 유일해야 합니다.

최상위 문서 필드

필드 타입 기본값 (생략 시) 설명
formatstring"story-engine/turn-source/1"명시 시 반드시 "story-engine/turn-source/1"이어야 합니다 (그 외 unsupported_format).
namestring""스토리 표시 이름.
default_state_accessstring"external"states[].access 생략 시 적용할 기본 접근 권한: "external" 또는 "internal".
typesarray[]명목 값 타입(id, name, value_type) 선언 목록.
entitiesarray[]상태 소유자 및 대사/행동 주체 엔티티(id, name, parents) 목록.
statesarray[]엔티티 소속 상태 슬롯(id, owner, declared_type, member, access, initial) 목록.
expressionsarray[]부작용 없는 순수 값 계산식 DAG 노드 목록.
snapshotsarray[]capture 턴이 불변 레코드로 기록할 StateRef 묶음 선언 목록.
turnsarray[]6종 주 작업 턴(dialogue, action, update, choice, capture, end) 목록.
groupsarray[]매개변수화된 재사용 노드 묶음(parameters, exits, nodes) 정의 목록.
usesarray[]그룹 인스턴스화 및 실제 엔티티·상태·출구 경로 결속(group, bindings, exits) 목록.

값 종류 (value_type), 엔티티 상속 및 상태 변경 권한

  • 8종 값 타입(types[].value_type):
    • 기본형: "bool"(별칭 "boolean"), "int"(별칭 "integer", 부호 있는 64비트 정수), "real"(유한 배정밀도 부동소수점), "string" — 문자열 축약형 또는 {"kind":"int"} 객체 형식 모두 허용.
    • 합성형: {"kind":"enum","enum_id":"GuildRank","members":["Bronze","Silver","Gold"]}, {"kind":"record","fields":{"hp":"int","tag":"string"}}, {"kind":"list","element":"int"}, {"kind":"optional","element":"string"}.
  • 엔티티 다중 상속 (entities[].parents) 및 isa 제약: 상위 엔티티에 비어 있지 않은 member 이름으로 선언된 상태는 컴파일 시 각 후손 엔티티의 독립 슬롯(<descendant_id>.<member>)으로 상속됩니다(런타임 슬롯 공유 없음). 후손은 동일한 declared_type으로 초기값을 재정의할 수 있으며 순환 상속은 entity_cycle로 거부됩니다. 그룹 엔티티 매개변수에 {"name":"shop","kind":"entity","isa":"MerchantBase"}를 지정하면 해당 엔티티 또는 후손만 결속되도록 정적·런타임 검증합니다.
  • 상태 접근 제어 (default_state_access & states[].access): "external" 상태는 스토리 내부 update 턴과 호스트 se_session_update 및 선택 응답 answer_json의 updates에서 모두 변경할 수 있습니다. "internal"로 지정된 상태는 작성된 update 턴과 호스트 읽기(se_session_read)는 허용하지만, 외부 호스트의 상태 갱신(se_session_update 및 선택 응답 updates)은 readonly_state 오류로 차단합니다.

순수 표현식 DAG 및 31개 연산자 전체 표

expressions[]의 각 노드는 상태를 변경하지 않는 비순환 그래프(DAG)이며 순환 참조는 컴파일 시 expression_cycle로 거부됩니다.

분류 연산자 (op) 인자 수 입력 필드 / 동작 규칙 반환 타입
리프 읽기 (4)literal0literal(또는 별칭 value) 필드의 상수 값을 반환리터럴 타입
state0state(StateRef) 슬롯의 현재 세션 값을 읽음상태 타입
snapshot0snapshot_record 또는 snapshot_record_expression에서 캡처 시점의 state 값을 조회상태 타입
visit0현재 실행 중인 턴의 누적 방문 횟수(1부터 시작) 반환int
산술 연산 (9)add, subtract, multiply2[a, b] 수치 연산. 둘 다 int면 int(오버플로 시 integer_overflow), 하나라도 real이면 realint | real
divide2a / b. 수치형 인자 2개, 결과는 항상 real (b == 0이면 division_by_zero)real
modulo2a % b. 두 인자 모두 int 전용 (b == 0이면 division_by_zero, INT64_MIN % -1 == 0)int
negate1-a. 부호 반전 (INT64_MIN 반전 시 integer_overflow)인자와 동일
min2[a, b] 두 수치형(int/real) 중 작은 값. 둘 다 int면 int, 하나라도 real이면 real (혼합 시 무손실 실수 표현 불가 정수는 numeric_precision)int | real
max2[a, b] 두 수치형(int/real) 중 큰 값. 둘 다 int면 int, 하나라도 real이면 real (혼합 시 무손실 실수 표현 불가 정수는 numeric_precision)int | real
clamp3[x, lo, hi] 수치형 x를 닫힌 구간 [lo, hi]로 제한. lo > hi이면 invalid_range로 거부(리터럴 역순 경계는 컴파일 진단으로도 검출). 모두 int면 int, 하나라도 real이면 realint | real
비교 연산 (6)equal, not_equal2호환 가능한 모든 타입(bool, int/real, string, 동일 enum, 합성형) 동등 비교bool
less, less_equal, greater, greater_equal2두 수치형(int/real) 인자의 대소 비교bool
논리·선택 (4)and, or2bool 전용 단락 평가(Short-circuit) 논리곱 / 논리합bool
not1bool 전용 논리 부정bool
select3[cond, t, f] 삼항 선택. cond에 따라 t 또는 f 한쪽만 지연 평가공통 타입
문자열 (1)concat2두 string 인자를 순서대로 연결string
수치 변환 (7)to_real, rounded_real1to_real은 무손실 변환만 허용(정밀도 손실 시 numeric_precision), rounded_real은 가장 가까운 double로 반올림real
to_integer1소수부가 0인 수치만 int로 정확히 변환(소수부 존재 시 numeric_precision)int
floor, ceil, truncate, round1수치형을 각각 내림, 올림, 버림, 반올림(0.5는 0에서 먼 쪽)하여 int로 변환int

6종 턴 (turns[]), 4종 경로 (Route) 및 구조화 페이로드

턴 kind 필수 필드 선택 필드 실행 의미
"dialogue"id, kind, nextname, actor, text, payload/payload_expr화자 대사를 방출하고 next 경로를 해석합니다.
"action"id, kind, nextname, actor, text, payload/payload_expr연출·행동 이벤트를 방출하고 next 경로를 해석합니다.
"update"id, kind, updates, nextname턴 진입 시점 상태 기준으로 updates[](set/add/subtract)를 원자적 일괄 적용합니다(페이로드 불가).
"choice"id, kind, choicesname, text, payload/payload_expr각 옵션의 when 조건을 평가해 적격 choices[]와 request ID를 발행하고 "awaiting_choice"로 전환합니다.
"capture"id, kind, snapshot, nextname선언된 스냅샷의 불변 레코드("<snapshot>#N")를 생성합니다(페이로드 불가).
"end"id, kindname, payload/payload_expr스토리를 종료하고 세션 단계를 "ended"로 전환합니다.
  • 4종 경로(Route): ① 직접 턴 이동("greet" 또는 {"turn":"window","use":"b_visit"}), ② 조건 분기({"if":"bool_expr","then":...,"else":...}), ③ 시드 기반 가중 무작위 분기({"weighted":[{"when":"...","weight":"...","next":...}]}), ④ 그룹 출구 탈출({"exit":"bought"}).
  • 구조화 페이로드(payload / payload_expr): 가시 턴(dialogue, action, choice, end)과 choices[] 각 항목에 연출 메타데이터(카메라 태그, 음성 ID, UI 레코드 등)를 명목 타입 검사와 함께 첨부할 수 있습니다: "payload": {"declared_type": "Cue", "value": ...} 또는 "payload_expr": "cue_expr_id" (둘 중 하나만 지정).

04 · Story Authoring Format (story-engine/turn-source/1)

se_compile and storyc consume a strict UTF-8 JSON document with format identifier story-engine/turn-source/1. Unknown keys, duplicate object keys, malformed UTF-8 sequences, and identifiers containing \u0000 (NUL) are rejected immediately. Every declared object, choice option, and explicitly named route id must be globally unique across the entire story.

Top-Level Document Fields

Field Type Default (if omitted) Description
formatstring"story-engine/turn-source/1"Must equal "story-engine/turn-source/1" when present (otherwise unsupported_format).
namestring""Human-readable story title.
default_state_accessstring"external"Default mutability policy when states[].access is omitted: "external" or "internal".
typesarray[]Nominal value type declarations (id, name, value_type).
entitiesarray[]Entity declarations owning states and acting in turns (id, name, parents).
statesarray[]Typed state slots bound to an owner entity (id, owner, declared_type, member, access, initial).
expressionsarray[]Side-effect-free pure expression DAG nodes.
snapshotsarray[]Snapshot schemas listing StateRef targets captured by capture turns.
turnsarray[]Primary execution turns (dialogue, action, update, choice, capture, end).
groupsarray[]Reusable parameterized subgraphs (parameters, exits, nodes).
usesarray[]Group instantiations binding concrete entities/states and exit routes (group, bindings, exits).

Value Kinds, Entity Inheritance & State Access Control

  • 8 Value Kinds (types[].value_type):
    • Primitives: "bool" (alias "boolean"), "int" (alias "integer", signed 64-bit), "real" (finite IEEE-754 double), "string"—accepted either as shorthand strings or {"kind":"int"} objects.
    • Composites: {"kind":"enum","enum_id":"GuildRank","members":["Bronze","Silver","Gold"]}, {"kind":"record","fields":{"hp":"int","tag":"string"}}, {"kind":"list","element":"int"}, {"kind":"optional","element":"string"}.
  • Entity Inheritance (entities[].parents) & Group isa Contract: Ancestor states with a non-empty member name are inherited at compile time into independent per-descendant slots (<descendant_id>.<member>; runtime slots are never aliased). Descendants may override initial values if declared_type matches; cycles fail with entity_cycle. Group entity parameters support {"name":"shop","kind":"entity","isa":"MerchantBase"}, statically and dynamically enforcing subtype compatibility.
  • State Mutability (default_state_access & states[].access): "external" states allow authored update turns as well as host writes via se_session_update and choice answer_json updates. "internal" states remain readable via se_session_read and mutable by authored update turns, but reject external host writes (se_session_update and choice updates) with readonly_state.

Pure Expression DAG & All 31 Operators

Nodes in expressions[] form a pure, side-effect-free DAG; cycles are rejected at compile time with expression_cycle.

Category Operator (op) Arity Inputs & Evaluation Semantics Result Type
Leaf Reads (4)literal0Returns constant in literal (or input alias value)Literal type
state0Reads current session value of state (StateRef)State type
snapshot0Reads captured state from snapshot_record or dynamic snapshot_record_expressionState type
visit0Returns 1-based cumulative visit count of the currently executing turnint
Arithmetic (9)add, subtract, multiply2Numeric [a, b]. Returns int if both are int (integer_overflow on overflow), else realint | real
divide2Numeric a / b; always yields real (division_by_zero if b == 0)real
modulo2Integer-only a % b (division_by_zero if b == 0; INT64_MIN % -1 == 0)int
negate1Unary -a (integer_overflow on INT64_MIN)Same as input
min2Numeric [a, b] smaller operand; returns int if both are int, else real (mixed int/real requires lossless double conversion, else numeric_precision)int | real
max2Numeric [a, b] larger operand; returns int if both are int, else real (mixed int/real requires lossless double conversion, else numeric_precision)int | real
clamp3Numeric [x, lo, hi] clamped to closed interval [lo, hi]; rejects lo > hi with invalid_range (also diagnosed statically for comparable literal bounds). Returns int if all three are int, else realint | real
Comparison (6)equal, not_equal2Structural equality across compatible bool, numeric, string, matching enum, record, list, optionalbool
less, less_equal, greater, greater_equal2Relational comparison across numeric (int / real) operandsbool
Logic & Select (4)and, or2Short-circuit boolean conjunction and disjunctionbool
not1Boolean negationbool
select3[cond, t, f] lazy ternary conditional; evaluates only the selected branchCommon type
String (1)concat2Concatenates two string operandsstring
Conversion (7)to_real, rounded_real1to_real requires lossless conversion (else numeric_precision); rounded_real rounds large int64 to nearest doublereal
to_integer1Exact conversion to int; fails with numeric_precision if fractional part is non-zeroint
floor, ceil, truncate, round1Converts numeric input to int via floor, ceiling, truncation toward zero, or half-away-from-zero roundingint

6 Primary Turn Kinds, 4 Route Forms & Structured Payloads

Turn kind Required Fields Optional Fields Runtime Behavior
"dialogue"id, kind, nextname, actor, text, payload/payload_exprEmits spoken line and resolves next route.
"action"id, kind, nextname, actor, text, payload/payload_exprEmits stage direction / gameplay cue and resolves next route.
"update"id, kind, updates, nextnameEvaluates all updates[] (set, add, subtract) against pre-turn state and commits atomically (no payload allowed).
"choice"id, kind, choicesname, text, payload/payload_exprFilters choices[] by when predicates, issues a request ID, and enters "awaiting_choice".
"capture"id, kind, snapshot, nextnameRecords an immutable snapshot record ("<snapshot>#N") of declared states (no payload allowed).
"end"id, kindname, payload/payload_exprTerminates traversal and transitions session phase to "ended".
  • 4 Route Forms (Route): (1) Direct turn target ("greet" or {"turn":"window","use":"b_visit"}), (2) Conditional branch ({"if":"bool_expr","then":...,"else":...}), (3) Seeded weighted random branch ({"weighted":[{"when":"...","weight":"...","next":...}]}), and (4) Group exit ({"exit":"bought"}).
  • Structured Payloads (payload / payload_expr): Observable turns (dialogue, action, choice, end) and individual choices[] options accept either "payload": {"declared_type": "Cue", "value": ...} or "payload_expr": "cue_expr_id" (mutually exclusive) for type-checked presentation metadata such as camera cues, audio keys, or UI tags.

04 · ストーリー記述フォーマット (story-engine/turn-source/1)

se_compile および storyc は厳格な UTF-8 JSON オブジェクト形式 story-engine/turn-source/1 を入力として受け取ります。未定義フィールド、重複キー、不正な UTF-8 シーケンス、\u0000(NUL)を含む識別子は即座に拒否され、すべての宣言オブジェクト・選択肢・名前付きルートの id はストーリー全体で一意である必要があります。

トップレベルフィールド

フィールド 型 省略時の既定値 説明
formatstring"story-engine/turn-source/1"指定する場合は "story-engine/turn-source/1" 必須(それ以外は unsupported_format)。
namestring""ストーリーの表示名。
default_state_accessstring"external"states[].access 省略時の既定アクセス権:"external" または "internal"。
typesarray[]公称値型(id, name, value_type)の宣言配列。
entitiesarray[]状態の所有者および発話・行動主体となるエンティティ(id, name, parents)配列。
statesarray[]エンティティに属する状態スロット(id, owner, declared_type, member, access, initial)配列。
expressionsarray[]副作用を持たない純粋な式 DAG ノード配列。
snapshotsarray[]capture ターンで不変レコードとして記録する StateRef 群の宣言配列。
turnsarray[]6 種類の主要ターン(dialogue, action, update, choice, capture, end)配列。
groupsarray[]パラメータ化された再利用サブグラフ(parameters, exits, nodes)定義配列。
usesarray[]グループの実体化および引数・出口ルートの束縛(group, bindings, exits)配列。

値の型・エンティティ継承・状態アクセス制御

  • 8 種類の値型(types[].value_type):
    • プリミティブ型:"bool"(別名 "boolean")、"int"(別名 "integer"、符号付き 64 ビット整数)、"real"(有限 IEEE-754 倍精度浮動小数点数)、"string"(短縮文字列または {"kind":"int"} 形式)。
    • 複合型:{"kind":"enum","enum_id":"GuildRank","members":["Bronze","Silver","Gold"]}、{"kind":"record","fields":{"hp":"int","tag":"string"}}、{"kind":"list","element":"int"}、{"kind":"optional","element":"string"}。
  • エンティティ継承(entities[].parents)と isa 制約: 親エンティティで非空の member を持つ状態は、コンパイル時に各子孫の独立スロット(<descendant_id>.<member>)として継承されます(実行時のスロット共有なし)。循環継承は entity_cycle で拒否されます。グループ引数 {"name":"shop","kind":"entity","isa":"MerchantBase"} により派生関係の静的・動的検証が行われます。
  • 状態の書き換え制御(default_state_access & states[].access): "external" は update ターンとホスト更新(se_session_update および選択応答 answer_json の updates)の双方から変更可能です。"internal" は update ターンおよび se_session_read を許可しつつ、ホストからの外部更新(se_session_update・選択応答 updates)を readonly_state エラーで拒否します。

純粋式 DAG と全 31 演算子一覧

expressions[] は副作用のない有向非巡回グラフ(DAG)であり、循環参照はコンパイル時に expression_cycle で拒否されます。

分類 演算子(op) 引数数 入力と評価規則 結果型
リーフ参照 (4)literal0literal(別名 value)の定数値を返すリテラル型
state0state(StateRef)の現在のセッション値を読み取る状態の型
snapshot0snapshot_record または snapshot_record_expression から記録時点の state 値を参照状態の型
visit0現在実行中のターンの累積訪問回数(1 始まり)を返すint
算術演算 (9)add, subtract, multiply2数値 [a, b]。両方 int なら int(桁あふれは integer_overflow)、いずれかが real なら realint | real
divide2数値 a / b。常に real を返す(b == 0 は division_by_zero)real
modulo2整数専用の a % b(b == 0 は division_by_zero、INT64_MIN % -1 == 0)int
negate1単項 -a(INT64_MIN 反転は integer_overflow)入力と同一
min2数値 [a, b] の小さい方の値。両方 int なら int、いずれかが real なら real(混在時に無損失変換できない整数は numeric_precision)int | real
max2数値 [a, b] の大きい方の値。両方 int なら int、いずれかが real なら real(混在時に無損失変換できない整数は numeric_precision)int | real
clamp3数値 [x, lo, hi] を閉区間 [lo, hi] に制限。lo > hi は invalid_range で拒否(比較可能なリテラル境界の逆転はコンパイル時にも検出)。すべて int なら int、いずれかが real なら realint | real
比較演算 (6)equal, not_equal2互換性のあるすべての型(bool, 数値, string, 同一 enum, 複合型)の等価比較bool
less, less_equal, greater, greater_equal2数値(int / real)間の大小比較bool
論理・選択 (4)and, or2bool 専用の短絡評価(ショートサーキット)論理積・論理和bool
not1bool 専用の論理否定bool
select3[cond, t, f] 条件選択。cond に応じ選ばれた側のみ遅延評価共通型
文字列 (1)concat22 つの string を連結string
数値変換 (7)to_real, rounded_real1to_real は無損失変換のみ許可(精度損失時は numeric_precision)、rounded_real は最近接 double に丸めるreal
to_integer1小数部が 0 の数値のみ int へ厳密変換(端数がある場合は numeric_precision)int
floor, ceil, truncate, round1床関数・天井関数・ゼロ方向切り捨て・四捨五入(0.5 は 0 から遠い側)で int に変換int

6 種類のターン(turns[])・4 種類のルート・構造化ペイロード

ターン kind 必須フィールド 任意フィールド 実行時の動作
"dialogue"id, kind, nextname, actor, text, payload/payload_expr台詞を出力し next ルートを解決します。
"action"id, kind, nextname, actor, text, payload/payload_expr演出・アクションを出力し next ルートを解決します。
"update"id, kind, updates, nextnameターン開始時点の状態を基準に updates[](set/add/subtract)を一括適用します(ペイロード不可)。
"choice"id, kind, choicesname, text, payload/payload_exprwhen 条件を満たす choices[] と request ID を返し、"awaiting_choice" に遷移します。
"capture"id, kind, snapshot, nextname指定スナップショットの不変レコード("<snapshot>#N")を生成します(ペイロード不可)。
"end"id, kindname, payload/payload_exprストーリーを終了し、セッションを "ended" に遷移させます。
  • 4 種類のルート(Route): ① 単一ターン遷移("greet" または {"turn":"window","use":"b_visit"})、② 条件分岐({"if":"bool_expr","then":...,"else":...})、③ シード乱数による重み付き抽選({"weighted":[{"when":"...","weight":"...","next":...}]})、④ グループ出口({"exit":"bought"})。
  • 構造化ペイロード(payload / payload_expr): 可視ターン(dialogue, action, choice, end)および choices[] の各項目に "payload": {"declared_type": "Cue", "value": ...} または "payload_expr": "cue_expr_id"(排他指定)を付与し、カメラ演出や音声キー等の型付きメタデータを渡せます。

05 · C ABI 레퍼런스 (story_engine/story_engine.h)

Story Engine 공유 라이브러리는 C++ 예외가 경계를 넘지 않도록 완벽히 격리된 27개의 C11 ABI 함수만 외부 심볼로 노출합니다. 런타임 초기화 시 se_abi_version() == 1u를 확인하여 로드된 바이너리의 ABI 호환성을 검증하십시오.

상수 매크로 및 상태 코드 (se_status)

심볼 / 상수 정의 / 값 설명
se_abi_version()1u (ABI Version 1)런타임에 로드된 공유 라이브러리의 공개 C ABI 버전 번호.
SE_MAX_JSON_BYTES8,388,608 (8 MiB)모든 UTF-8 JSON 및 텍스트 입출력의 최대 바이트 길이 (종단 NUL 제외).
SE_MAX_ARTIFACT_BYTES8,392,704 (8 MiB + 4 KiB)se_program_load가 허용하는 불투명 바이너리 아티팩트 최대 바이트 크기.
SE_MAX_CHECKPOINT_BYTES16,777,216 (16 MiB)se_session_load가 허용하는 불투명 바이너리 체크포인트 최대 바이트 크기.
SE_OK0호출 성공. 출력 se_buffer에 결과 JSON 또는 바이너리 바이트열 기록.
SE_ERROR1JSON 문법·스키마 오류, invalid_phase, pending_choice, readonly_state, 런타임 산술·범위 오류(invalid_range 등).
SE_INVALID_ARGUMENT2필수 포인터 NULL, 크기 한도 초과, 비어 있지 않은 출력 se_buffer 전달, max_steps == 0 등.
SE_INVALID_HANDLE30, 이미 해제된 핸들, 존재하지 않거나 종류가 다른 핸들(se_program/se_session/se_object) 전달.
SE_COMPILE_ERROR4정적 의미 검증 실패 또는 손상된 아티팩트(error.code == "compile_error", diagnostics 배열 동반).
SE_OUT_OF_MEMORY5메모리 할당 실패. 오류 JSON 버퍼조차 할당되지 못해 data == NULL일 수 있으므로 상태 코드를 먼저 확인하십시오.

se_buffer 및 핸들 소유권 규칙

  • 출력 버퍼 {0} 초기화 필수: 모든 출력 se_buffer는 호출 전 반드시 se_buffer out = {0}; 상태여야 합니다. data != NULL이거나 size != 0인 버퍼를 전달하면 기존 데이터를 덮어쓰지 않고 즉시 SE_INVALID_ARGUMENT를 반환합니다.
  • 해제 의무 및 멱등성: 성공 여부와 무관하게 out.data != NULL이면 사용 후 반드시 se_buffer_release(&out)로 해제하십시오(호출자의 free() 사용 금지, 구조체 값 복사로 중복 해제 금지). 해제된 구조체는 자동으로 {0}으로 리셋됩니다.
  • 텍스트 JSON vs 불투명 바이너리 출력: JSON 출력 함수는 편의를 위해 data[size]에 여분의 '\0'을 덧붙이지만(size는 종단 NUL 제외), se_program_artifact와 se_session_save가 반환하는 불투명 바이너리 버퍼는 내부에 0x00 바이트를 포함할 수 있으므로 strlen을 쓰지 말고 정확히 size 바이트만 처리하십시오.
  • 핸들 수명 및 스레드 안전성: se_program, se_session, se_object는 모두 0이 아닌 uint64_t ID이며 한 라이브러리 인스턴스 내에서 재사용되지 않습니다. 엔진 내부 레지스트리가 동시 C ABI 호출을 직렬화하며, 진행 중인 호출이 참조하는 입력 메모리와 출력 se_buffer 구조체는 호출 완료 전까지 변경·해제해서는 안 됩니다.

전체 27개 공개 함수 요약 표

분류 함수 이름 핵심 동작 및 출력 계약
버전·버퍼 (2)se_abi_version로드된 라이브러리의 ABI 버전(1u) 반환.
se_buffer_release엔진 소유 se_buffer 메모리를 해제하고 {0}으로 초기화 (NULL/빈 버퍼에 멱등).
프로그램 (6)se_compilestory-engine/turn-source/1 UTF-8 JSON을 검증·컴파일해 se_program 핸들과 진단 배열 반환.
se_program_load불투명 바이너리 아티팩트(artifact, size)를 검증·로드하여 se_program 핸들 발행.
se_program_artifact컴파일된 프로그램을 불투명 바이너리 바이트 버퍼(data + size)로 직렬화.
se_program_inspect프로그램 지문, 타입, 엔티티 상속 계층, 상태, 스냅샷, 그룹, 턴 카탈로그 메타데이터 JSON 반환.
se_program_retain독립적으로 해제 가능한 새로운 se_program 핸들 발행.
se_program_releasese_program 핸들 해제 (활성 세션은 내부적으로 프로그램을 유지).
세션 수명·저장 (6)se_session_create"idle" 단계의 새 세션 생성 (seed로 가중 무작위 경로 난수 제어).
se_session_clone추론 실행·즉시 롤백을 위한 독립 런타임 세션 복제본 생성(외부 객체 임대는 제외).
se_session_save현재 세션을 불투명 바이너리 체크포인트 바이트 버퍼(data + size)로 직렬화.
se_session_load원본 program과 대조 검증하여 불투명 바이너리 체크포인트로부터 새 세션 복원.
se_session_retain동일 세션을 가리키는 독립 se_session 핸들 추가 발행.
se_session_releasese_session 핸들 해제 (진행 중인 호출이 있으면 완료 후 종료 대기열로 이동).
실행·상태·평가 (6)se_session_start"idle" 세션에 진입 턴/그룹 결속(entry_json)을 설정해 "ready"로 전환.
se_session_advance주 작업 턴 1개를 실행하거나 answer_json으로 대기 중인 선택을 완료 후 후속 턴 1개 실행. 선택 응답에 선택적 updates(외부 상태 일괄 갱신; internal 상태 쓰기는 readonly_state로 거부)와 bindings를 포함하면 option.next 경로 해석 전에 원자적으로 먼저 적용되어 결과 changes 맨 앞에 기록되며, 어떤 오류라도 발생하면 호출 전체가 awaiting_choice 상태로 완전 롤백됨.
se_session_advance_to_yield내부 update/capture 턴을 max_steps 예산 내에서 연속 실행하여 첫 가시 턴 또는 출구에서 반환. 선택 응답의 선택적 updates/bindings 선행 원자적 적용·changes 선두 기록·실패 시 awaiting_choice 전체 롤백 계약을 동일하게 지원.
se_session_read세션 진행 없이 지정한 StateRef 배열의 현재 정규화 값 맵을 원자적으로 조회.
se_session_update호스트에서 외부 변경 가능(external) 상태들을 원자적 배치로 일괄 갱신.
se_session_evaluate부작용 없이 현재 세션 상태 기준으로 expression_id를 순수 평가.
스냅샷·뷰·정리 (3)se_session_read_snapshotcapture 턴이 생성한 불변 스냅샷 레코드("<snapshot>#N") 값 조회.
se_session_view현재 phase, next, pending_turn, request, version 요약 조회.
se_session_collect활성 세션 내부의 불필요해진 객체를 budget 한도 내에서 정리.
외부 객체·전역 정리 (4)se_object_retain상태·엔티티·스냅샷 레코드 ID에 대한 외부 보유 핸들(se_object) 획득.
se_object_read보유한 상태 se_object 값을 JSON으로 조회 (세션 핸들 해제 후에도 읽기 가능).
se_object_release외부 se_object 보유 핸들 해제.
se_collect_retired해제된 은퇴 세션들의 물리 자원을 budget 한도 내에서 회수.

전체 C 헤더 프로토타입 (27개 심볼)

typedef uint64_t se_program;
typedef uint64_t se_session;
typedef uint64_t se_object;

typedef enum se_status {
    SE_OK = 0,
    SE_ERROR = 1,
    SE_INVALID_ARGUMENT = 2,
    SE_INVALID_HANDLE = 3,
    SE_COMPILE_ERROR = 4,
    SE_OUT_OF_MEMORY = 5
} se_status;

typedef struct se_buffer {
    char *data;
    size_t size;
} se_buffer;

#define SE_MAX_JSON_BYTES       ((size_t)8 * 1024 * 1024)
#define SE_MAX_ARTIFACT_BYTES   (SE_MAX_JSON_BYTES + (size_t)4096)
#define SE_MAX_CHECKPOINT_BYTES ((size_t)16 * 1024 * 1024)

/* 1–2: ABI Version & Buffer Ownership */
SE_API uint32_t  se_abi_version(void);
SE_API void      se_buffer_release(se_buffer *buffer);

/* 3–8: Program Compilation, Artifact I/O, Inspection & Lifetime */
SE_API se_status se_compile(const char *source_json, se_program *program, se_buffer *diagnostics);
SE_API se_status se_program_load(const void *artifact, size_t size, se_program *program, se_buffer *diagnostics);
SE_API se_status se_program_artifact(se_program program, se_buffer *artifact);
SE_API se_status se_program_inspect(se_program program, se_buffer *result);
SE_API se_status se_program_retain(se_program program, se_program *retained);
SE_API se_status se_program_release(se_program program);

/* 9–14: Session Creation, Cloning, Opaque Checkpoints & Lifetime */
SE_API se_status se_session_create(se_program program, uint64_t seed, se_session *session, se_buffer *result);
SE_API se_status se_session_clone(se_session source, se_session *cloned, se_buffer *result);
SE_API se_status se_session_save(se_session session, se_buffer *checkpoint);
SE_API se_status se_session_load(se_program program, const void *checkpoint, size_t size, se_session *session, se_buffer *result);
SE_API se_status se_session_retain(se_session session, se_session *retained);
SE_API se_status se_session_release(se_session session);

/* 15–23: Turn Stepping, State Access, Pure Evaluation, Snapshots & Maintenance */
SE_API se_status se_session_start(se_session session, const char *entry_json, se_buffer *result);
SE_API se_status se_session_advance(se_session session, const char *answer_json, se_buffer *result);
SE_API se_status se_session_advance_to_yield(se_session session, const char *answer_json, size_t max_steps, se_buffer *result);
SE_API se_status se_session_read(se_session session, const char *refs_json, se_buffer *result);
SE_API se_status se_session_update(se_session session, const char *assignments_json, const char *bindings_json, se_buffer *result);
SE_API se_status se_session_evaluate(se_session session, const char *expression_id, const char *bindings_json, se_buffer *result);
SE_API se_status se_session_read_snapshot(se_session session, const char *record_id, se_buffer *result);
SE_API se_status se_session_view(se_session session, se_buffer *result);
SE_API se_status se_session_collect(se_session session, size_t budget, se_buffer *result);

/* 24–27: External Object Leases & Retired Session Reclamation */
SE_API se_status se_object_retain(se_session session, const char *object_id, se_object *object, se_buffer *result);
SE_API se_status se_object_read(se_object object, se_buffer *result);
SE_API se_status se_object_release(se_object object);
SE_API se_status se_collect_retired(size_t budget, se_buffer *result);

05 · C ABI Reference (story_engine/story_engine.h)

The Story Engine shared library exports exactly 27 C11 functions across an exception-safe C boundary. Verify se_abi_version() == 1u during host startup before calling other entry points.

Limit Constants & Status Codes (se_status)

Symbol / Constant Value Contract
se_abi_version()1u (ABI Version 1)Runtime C ABI version number exposed by the loaded shared library.
SE_MAX_JSON_BYTES8,388,608 (8 MiB)Maximum byte length for UTF-8 JSON and text inputs/outputs (excluding trailing NUL).
SE_MAX_ARTIFACT_BYTES8,392,704 (8 MiB + 4 KiB)Maximum input byte size accepted by se_program_load.
SE_MAX_CHECKPOINT_BYTES16,777,216 (16 MiB)Maximum opaque binary checkpoint byte size accepted by se_session_load.
SE_OK0Operation succeeded; output se_buffer holds the result payload.
SE_ERROR1JSON syntax/schema error, invalid_phase, pending_choice, readonly_state, or runtime math/range fault (e.g., invalid_range).
SE_INVALID_ARGUMENT2Required pointer is NULL, size cap exceeded, non-empty output se_buffer, or max_steps == 0.
SE_INVALID_HANDLE3Handle is 0, already released, unknown, or of the wrong handle kind.
SE_COMPILE_ERROR4Static semantic validation or artifact check failed (error.code == "compile_error" with diagnostics[]).
SE_OUT_OF_MEMORY5Allocation failed; output se_buffer may remain {0}—always inspect se_status and buf.data != NULL first.

se_buffer Ownership & Handle Concurrency Rules

  • Zero-Initialize Output Buffers: Every output se_buffer must be initialized to {0} before a call. Passing a non-empty buffer (data != NULL or size != 0) returns SE_INVALID_ARGUMENT without overwriting existing data.
  • Release via se_buffer_release: Release every populated buffer (on both SE_OK and error envelopes) with se_buffer_release(&buf) before reuse. It is idempotent on NULL or {0} and resets the struct to {0}. Never pass buf.data to free() or shallow-copy the struct for multiple releases.
  • Text JSON vs. Opaque Binary Outputs: JSON outputs append a convenience NUL byte at data[size] (size excludes trailing NUL). Opaque binary outputs from se_program_artifact and se_session_save may contain embedded 0x00 bytes—always use exact size.
  • Handles & Thread Safety: se_program, se_session, and se_object are distinct nonzero uint64_t IDs never reused within a library instance. Concurrent C ABI calls on handles are synchronized internally; caller input pointers and output se_buffer structs must remain valid and unmutated for the duration of an active call.

Categorized Summary of All 27 Exported Functions

Category Function Behavior & Output Contract
ABI & Buffer (2)se_abi_versionReturns 1u for ABI v1 compatibility verification.
se_buffer_releaseFrees engine-owned buffer memory and resets *buffer to {0}.
Program (6)se_compileCompiles story-engine/turn-source/1 UTF-8 JSON into a se_program handle plus diagnostics.
se_program_loadValidates and loads an opaque binary artifact (artifact, size) into a se_program handle.
se_program_artifactExports a compiled program as an opaque binary byte buffer (data + size).
se_program_inspectReturns JSON catalog of fingerprint, types, entities/ancestors, states, snapshots, groups, uses, and turns.
se_program_retainPublishes a distinct se_program handle requiring its own se_program_release.
se_program_releaseReleases a se_program handle (live sessions retain program backing independently).
Session Lifecycle (6)se_session_createCreates an "idle" session initialized with default states and deterministic RNG seed.
se_session_cloneCreates an independent runtime copy (clearing external leases) for speculative branching or rollback.
se_session_saveSerializes session state into an opaque binary checkpoint byte buffer (data + size).
se_session_loadRestores a session from an opaque binary checkpoint verified against program.
se_session_retainPublishes a distinct se_session handle referencing the same session.
se_session_releaseReleases a se_session handle (defers teardown until in-flight calls complete).
Execution & State (6)se_session_startConfigures entry turn and optional group bindings on an "idle" session, entering "ready".
se_session_advanceExecutes one primary turn, or resolves a pending choice via answer_json and executes its follow-up turn. Choice answer_json accepts optional updates (external state batch assignments; internal state writes rejected with readonly_state) and optional bindings, evaluated atomically before option.next routing, prepended to result changes, and rolled back completely to awaiting_choice on any error.
se_session_advance_to_yieldExecutes internal update/capture turns atomically up to max_steps until an observable turn or exit; supports the same atomic choice updates/bindings, prepended changes, and full rollback to awaiting_choice on error.
se_session_readAtomically reads canonical values for an array of StateRef targets without advancing execution.
se_session_updateApplies an atomic host batch of assignments to "external" states when no choice is pending.
se_session_evaluateEvaluates expression_id purely against current session state without side effects.
Snapshots & GC (3)se_session_read_snapshotReads immutable values stored in a captured snapshot record ("<snapshot>#N").
se_session_viewReturns current phase, next, pending_turn, request, and mutation version.
se_session_collectRuns bounded session maintenance up to budget work units.
Object Leases & Global GC (4)se_object_retainAcquires an external se_object hold on a state, entity, or snapshot record ID.
se_object_readReads a retained state’s canonical JSON value (valid even after se_session_release).
se_object_releaseReleases an external se_object handle.
se_collect_retiredReclaims retired closed sessions up to budget work units.

Exact C Prototypes (All 27 Symbols)

typedef uint64_t se_program;
typedef uint64_t se_session;
typedef uint64_t se_object;

typedef enum se_status {
    SE_OK = 0,
    SE_ERROR = 1,
    SE_INVALID_ARGUMENT = 2,
    SE_INVALID_HANDLE = 3,
    SE_COMPILE_ERROR = 4,
    SE_OUT_OF_MEMORY = 5
} se_status;

typedef struct se_buffer {
    char *data;
    size_t size;
} se_buffer;

#define SE_MAX_JSON_BYTES       ((size_t)8 * 1024 * 1024)
#define SE_MAX_ARTIFACT_BYTES   (SE_MAX_JSON_BYTES + (size_t)4096)
#define SE_MAX_CHECKPOINT_BYTES ((size_t)16 * 1024 * 1024)

/* 1–2: ABI Version & Buffer Ownership */
SE_API uint32_t  se_abi_version(void);
SE_API void      se_buffer_release(se_buffer *buffer);

/* 3–8: Program Compilation, Artifact I/O, Inspection & Lifetime */
SE_API se_status se_compile(const char *source_json, se_program *program, se_buffer *diagnostics);
SE_API se_status se_program_load(const void *artifact, size_t size, se_program *program, se_buffer *diagnostics);
SE_API se_status se_program_artifact(se_program program, se_buffer *artifact);
SE_API se_status se_program_inspect(se_program program, se_buffer *result);
SE_API se_status se_program_retain(se_program program, se_program *retained);
SE_API se_status se_program_release(se_program program);

/* 9–14: Session Creation, Cloning, Opaque Checkpoints & Lifetime */
SE_API se_status se_session_create(se_program program, uint64_t seed, se_session *session, se_buffer *result);
SE_API se_status se_session_clone(se_session source, se_session *cloned, se_buffer *result);
SE_API se_status se_session_save(se_session session, se_buffer *checkpoint);
SE_API se_status se_session_load(se_program program, const void *checkpoint, size_t size, se_session *session, se_buffer *result);
SE_API se_status se_session_retain(se_session session, se_session *retained);
SE_API se_status se_session_release(se_session session);

/* 15–23: Turn Stepping, State Access, Pure Evaluation, Snapshots & Maintenance */
SE_API se_status se_session_start(se_session session, const char *entry_json, se_buffer *result);
SE_API se_status se_session_advance(se_session session, const char *answer_json, se_buffer *result);
SE_API se_status se_session_advance_to_yield(se_session session, const char *answer_json, size_t max_steps, se_buffer *result);
SE_API se_status se_session_read(se_session session, const char *refs_json, se_buffer *result);
SE_API se_status se_session_update(se_session session, const char *assignments_json, const char *bindings_json, se_buffer *result);
SE_API se_status se_session_evaluate(se_session session, const char *expression_id, const char *bindings_json, se_buffer *result);
SE_API se_status se_session_read_snapshot(se_session session, const char *record_id, se_buffer *result);
SE_API se_status se_session_view(se_session session, se_buffer *result);
SE_API se_status se_session_collect(se_session session, size_t budget, se_buffer *result);

/* 24–27: External Object Leases & Retired Session Reclamation */
SE_API se_status se_object_retain(se_session session, const char *object_id, se_object *object, se_buffer *result);
SE_API se_status se_object_read(se_object object, se_buffer *result);
SE_API se_status se_object_release(se_object object);
SE_API se_status se_collect_retired(size_t budget, se_buffer *result);

05 · C ABI リファレンス (story_engine/story_engine.h)

Story Engine 共有ライブラリは C++ 例外が境界を越えない 全 27 個の C11 ABI 関数のみをエクスポートします。起動時に se_abi_version() == 1u を確認してバイナリの互換性を検証してください。

上限定数とステータスコード(se_status)

シンボル / 定数 値 契約内容
se_abi_version()1u (ABI Version 1)ロードされた共有ライブラリの C ABI バージョン番号。
SE_MAX_JSON_BYTES8,388,608 (8 MiB)UTF-8 JSON およびテキスト入出力の最大バイト長(終端 NUL を除く)。
SE_MAX_ARTIFACT_BYTES8,392,704 (8 MiB + 4 KiB)se_program_load が受け付ける不透明バイナリアーティファクトの最大サイズ。
SE_MAX_CHECKPOINT_BYTES16,777,216 (16 MiB)se_session_load が受け付ける不透明バイナリチェックポイントの最大サイズ。
SE_OK0成功。出力 se_buffer に結果 JSON またはバイナリデータが格納されます。
SE_ERROR1JSON 構文/スキーマ違反、invalid_phase、pending_choice、readonly_state、算術・範囲エラー(invalid_range 等)。
SE_INVALID_ARGUMENT2必須ポインタが NULL、サイズ上限超過、非空の出力 se_buffer、max_steps == 0 等。
SE_INVALID_HANDLE30、解放済みハンドル、未知の ID、または異なる種類のハンドルを指定した場合。
SE_COMPILE_ERROR4静的意味検証またはアーティファクト検証失敗(error.code == "compile_error"、diagnostics[] 同伴)。
SE_OUT_OF_MEMORY5メモリ確保失敗。出力バッファが {0} のままになる場合があるため必ず先に se_status を確認してください。

se_buffer 所有権とハンドルの並行安全性

  • 出力バッファの {0} 初期化: 出力用の se_buffer は呼び出し前に必ず {0} で初期化してください。空でないバッファを渡すと既存データを破壊せず即座に SE_INVALID_ARGUMENT を返します。
  • se_buffer_release による解放: 成功・失敗にかかわらず buf.data != NULL の場合は se_buffer_release(&buf) で解放してください(free() の直接呼び出しや構造体の浅いコピーによる二重解放は禁止)。解放後は {0} にリセットされます。
  • JSON 出力と不透明バイナリ出力の扱い: JSON 出力は data[size] に終端 '\0' が付与されます(size は終端を除く)。一方、se_program_artifact と se_session_save の出力は途中に 0x00 を含み得る不透明バイナリであるため、必ず size バイト数で処理してください。
  • ハンドル寿命とスレッド安全性: se_program・se_session・se_object は 0 以外の uint64_t であり再利用されません。C ABI 経由の並行呼び出しは内部で直列化されますが、呼び出し中の入力メモリおよび出力 se_buffer を別スレッドから変更・解放してはなりません。

全 27 公開関数カテゴリ別一覧

カテゴリ 関数名 動作と出力契約
ABI・バッファ (2)se_abi_versionABI バージョン(1u)を返す。
se_buffer_releaseエンジン所有の se_buffer を解放し {0} にリセットする(冪等)。
プログラム (6)se_compilestory-engine/turn-source/1 JSON を検証・コンパイルし se_program と診断配列を返す。
se_program_load不透明バイナリアーティファクト(artifact, size)を検証・ロードする。
se_program_artifactコンパイル済みプログラムを不透明バイナリバイトバッファ(data + size)へ書き出す。
se_program_inspectプログラム指紋・型・エンティティ階層・状態・スナップショット・グループ・ターンのカタログ JSON を返す。
se_program_retain個別に解放可能な新しい se_program ハンドルを発行する。
se_program_releasese_program ハンドルを解放する(稼働中セッションは内部参照を保持)。
セッション管理 (6)se_session_create"idle" 状態のセッションを生成する(seed で重み付き乱数分岐を制御)。
se_session_clone先読み実行や即時ロールバック用に独立したセッション複製を生成する(外部リースは引き継がない)。
se_session_saveセッション状態を不透明バイナリチェックポイント(data + size)として直列化する。
se_session_load元の program と照合検証した上で不透明バイナリチェックポイントからセッションを復元する。
se_session_retain同一セッションを参照する別個の se_session ハンドルを発行する。
se_session_releasese_session ハンドルを解放する。
進行・状態・評価 (6)se_session_start"idle" セッションの開始ターン/グループ束縛(entry_json)を設定し "ready" に遷移させる。
se_session_advance主要ターンを 1 つ実行するか、answer_json で選択を確定して後続ターンを 1 つ実行する。選択応答は任意の updates(外部状態の一括更新、internal 状態への書き込みは readonly_state で拒否)および bindings を受け付け、option.next ルート解決前にアトミックに適用して結果の changes 先頭へ追加する(エラー発生時は awaiting_choice へ完全ロールバック)。
se_session_advance_to_yield内部の update/capture を max_steps 予算内で連続実行し、最初の可視ターンまたは出口で返す。選択応答の updates/bindings 先行アトミック適用・changes 先頭記録・エラー時の awaiting_choice 完全ロールバック契約に同様に対応。
se_session_read進行を伴わず StateRef 配列の正規化値マップをアトミックに読み取る。
se_session_update選択待ちでない状態でホストから "external" 状態群を一括アトミック更新する。
se_session_evaluate副作用なしで expression_id を現在のセッション状態に対して純粋評価する。
スナップショット・回収 (3)se_session_read_snapshotcapture ターンが記録した不変レコード("<snapshot>#N")を参照する。
se_session_view現在の phase, next, pending_turn, request, version を取得する。
se_session_collect稼働中セッションの不要オブジェクトを budget 上限内で回収する。
外部オブジェクト・退役回収 (4)se_object_retain状態・エンティティ・スナップショットレコード ID の外部ハンドル(se_object)を保持する。
se_object_read保持した状態 se_object の現在値を読み取る(セッション解放後も有効)。
se_object_release外部 se_object ハンドルを解放する。
se_collect_retiredクローズ済みセッションの物理リソースを budget 上限内で回収する。

C ヘッダ全関数プロトタイプ(全 27 シンボル)

typedef uint64_t se_program;
typedef uint64_t se_session;
typedef uint64_t se_object;

typedef enum se_status {
    SE_OK = 0,
    SE_ERROR = 1,
    SE_INVALID_ARGUMENT = 2,
    SE_INVALID_HANDLE = 3,
    SE_COMPILE_ERROR = 4,
    SE_OUT_OF_MEMORY = 5
} se_status;

typedef struct se_buffer {
    char *data;
    size_t size;
} se_buffer;

#define SE_MAX_JSON_BYTES       ((size_t)8 * 1024 * 1024)
#define SE_MAX_ARTIFACT_BYTES   (SE_MAX_JSON_BYTES + (size_t)4096)
#define SE_MAX_CHECKPOINT_BYTES ((size_t)16 * 1024 * 1024)

/* 1–2: ABI Version & Buffer Ownership */
SE_API uint32_t  se_abi_version(void);
SE_API void      se_buffer_release(se_buffer *buffer);

/* 3–8: Program Compilation, Artifact I/O, Inspection & Lifetime */
SE_API se_status se_compile(const char *source_json, se_program *program, se_buffer *diagnostics);
SE_API se_status se_program_load(const void *artifact, size_t size, se_program *program, se_buffer *diagnostics);
SE_API se_status se_program_artifact(se_program program, se_buffer *artifact);
SE_API se_status se_program_inspect(se_program program, se_buffer *result);
SE_API se_status se_program_retain(se_program program, se_program *retained);
SE_API se_status se_program_release(se_program program);

/* 9–14: Session Creation, Cloning, Opaque Checkpoints & Lifetime */
SE_API se_status se_session_create(se_program program, uint64_t seed, se_session *session, se_buffer *result);
SE_API se_status se_session_clone(se_session source, se_session *cloned, se_buffer *result);
SE_API se_status se_session_save(se_session session, se_buffer *checkpoint);
SE_API se_status se_session_load(se_program program, const void *checkpoint, size_t size, se_session *session, se_buffer *result);
SE_API se_status se_session_retain(se_session session, se_session *retained);
SE_API se_status se_session_release(se_session session);

/* 15–23: Turn Stepping, State Access, Pure Evaluation, Snapshots & Maintenance */
SE_API se_status se_session_start(se_session session, const char *entry_json, se_buffer *result);
SE_API se_status se_session_advance(se_session session, const char *answer_json, se_buffer *result);
SE_API se_status se_session_advance_to_yield(se_session session, const char *answer_json, size_t max_steps, se_buffer *result);
SE_API se_status se_session_read(se_session session, const char *refs_json, se_buffer *result);
SE_API se_status se_session_update(se_session session, const char *assignments_json, const char *bindings_json, se_buffer *result);
SE_API se_status se_session_evaluate(se_session session, const char *expression_id, const char *bindings_json, se_buffer *result);
SE_API se_status se_session_read_snapshot(se_session session, const char *record_id, se_buffer *result);
SE_API se_status se_session_view(se_session session, se_buffer *result);
SE_API se_status se_session_collect(se_session session, size_t budget, se_buffer *result);

/* 24–27: External Object Leases & Retired Session Reclamation */
SE_API se_status se_object_retain(se_session session, const char *object_id, se_object *object, se_buffer *result);
SE_API se_status se_object_read(se_object object, se_buffer *result);
SE_API se_status se_object_release(se_object object);
SE_API se_status se_collect_retired(size_t budget, se_buffer *result);

06 · JSON 프로토콜 및 진단 스키마

불투명 바이너리 바이트 버퍼를 출력하는 se_program_artifact와 se_session_save를 제외한 모든 se_buffer 출력 함수는 표준 UTF-8 JSON 봉투(Envelope)를 반환합니다.

표준 응답 봉투 및 64비트 정수 직렬화

// 1) 성공 봉투 (status == SE_OK)
{ "ok": true, "result": { ... } }

// 2) 일반 런타임·입력 오류 봉투 (SE_ERROR / SE_INVALID_ARGUMENT 등)
{
  "ok": false,
  "error": {
    "code": "pending_choice",
    "subject": "",
    "message": "Cannot update state while awaiting choice"
  }
}

// 3) 컴파일·아티팩트 검증 실패 봉투 (status == SE_COMPILE_ERROR)
{
  "ok": false,
  "error": { "code": "compile_error", "subject": "", "message": "Compilation failed" },
  "diagnostics": [
    {
      "severity": "error",
      "code": "unknown_turn",
      "subject": "greet",
      "message": "Route target turn does not exist: missing_turn"
    }
  ]
}

정규화 64비트 정수 규칙: JavaScript, Godot JSON.parse_string 등 숫자를 배정밀도 실수(double)로 파싱하는 호스트에서 ±2^53 이상의 정밀도 손실을 막기 위해, 엔진의 모든 정규화 출력에서 int 값은 항상 십진수 문자열(예: {"kind":"int","value":"9223372036854775807"})로 직렬화됩니다.

세션 진입 (entry_json), 턴 실행 결과 및 선택 응답

  • 진입 요청 (se_session_start): 일반 턴 진입은 {"turn":"record_opening"}, 그룹 인스턴스 진입은 {"turn":"front","use":"a_visit"}, 열린 그룹 직접 진입은 {"turn":"window","group":"shop_flow","bindings":{"shop":"shop_b"}}를 전달합니다.
  • 선택지 응답 (answer_json): 세션이 "awaiting_choice"일 때 {"request":"choice#1","option":"pay_fee","updates":[{"target":"last_roll","value":17}],"bindings":{}}(선택적 updates·bindings)를 전달하면 외부 상태 일괄 갱신(updates, "internal" 상태 쓰기 시 readonly_state 거부)과 결속(bindings, 생략 시 현재 선택 프레임 결속 사용)을 option.next 경로 해석 및 후속 턴 실행 전에 원자적으로 적용하며, 변경 내역을 결과의 changes 배열 앞쪽에 순서대로 추가합니다. 선택 검증·상태 갱신·수식 평가·예산 한도 중 어느 단계에서든 실패하면 호출 전체가 완전히 롤백되어 "awaiting_choice"를 유지합니다(NULL이나 "{}" 전달 시 상태 변경 없이 현재 선택 프롬프트 재반환).
실행 결과 필드 타입 설명 (se_session_advance / se_session_advance_to_yield의 result)
phasestring도달한 세션 단계: "ready", "awaiting_choice", "ended", "idle".
executedstring실행된 주 작업 턴 ID (열린 그룹 출구로 바로 빠져나간 경우 "").
kindstring"dialogue", "action", "update", "choice", "capture", "end".
actor / textstring해석된 행위자 엔티티 ID 및 대사·지문·선택 안내 텍스트 (없으면 "").
payloadobject턴에 선언된 선택적 타입 지정 값 객체 (미선언 시 키 생략).
request / choicesstring / array"awaiting_choice"일 때 발행된 요청 ID와 적격 옵션 배열 [{"id","text","payload"?}].
changesarray상태 변경 내역 배열 [{"state","before","after"}] (선택 응답 updates가 앞쪽에 먼저 기록되고 후속 update 턴 변경이 순서대로 누적).
snapshot_recordstringcapture 실행으로 생성된 레코드 ID (예: "initial_ledger#1").
completed_choicestring선택 응답을 처리한 호출인 경우 완료된 선택 턴 ID, 그 외 "".
next / exitstring다음 예정 턴 ID 및 열린 그룹 탈출 시 도달한 출구 이름(exit).

프로그램 검사, 상태 조회/갱신 및 불투명 바이너리 계약

  • se_program_inspect: result에 format, name, fingerprint, default_state_access, types, entities(parents·ancestors 포함), states(inherited·inherited_from 포함), snapshots, groups, uses, turns(has_payload 포함) 카탈로그를 반환합니다.
  • se_session_read / se_session_update / se_session_evaluate:
    • se_session_read(s, "[\"coins\"]", &out) → {"ok":true,"result":{"coins":{"kind":"int","value":"40"}}}
    • se_session_update(s, "[{\"target\":\"coins\",\"op\":\"add\",\"value\":10}]", NULL, &out) → 변경 전후 [{"state","before","after"}] 반환 (배치 내 하나라도 실패하거나 readonly_state / pending_choice이면 전체 롤백)
    • se_session_evaluate(s, "effective_fee", NULL, &out) → {"ok":true,"result":{"value":{...},"declared_type":""}}
  • 주요 오류 코드 (error.code): 입력·핸들 검증(invalid_argument, invalid_handle, invalid_json, invalid_schema, unsupported_format, resource_limit), 단계·분기·상태 보호(invalid_phase, pending_choice, invalid_choice, no_choice, no_route, readonly_state, duplicate_target, step_budget_exceeded), 수식·범위 검증(division_by_zero, invalid_range — clamp의 lo > hi 역순 경계 시 발생하며 리터럴 경계는 컴파일 진단으로도 검출, integer_overflow / overflow, numeric_precision / numeric_range, type_mismatch), 바이너리 무결성(invalid_artifact, invalid_checkpoint, incompatible_program).
  • 불투명 바이너리 아티팩트 및 체크포인트 계약: se_program_artifact / se_program_load의 스토리 아티팩트와 se_session_save / se_session_load의 세션 체크포인트는 내부 레이아웃이 비공개인 불투명(opaque) 바이너리 바이트 버퍼(data + size)입니다. 호스트는 바이트열을 파싱하거나 수정하지 않고 size 바이트 그대로 저장·전송해야 하며, 로드 시 엔진이 원본 프로그램과의 일치 여부 및 무결성을 검증합니다(불일치·손상 시 각각 invalid_artifact, invalid_checkpoint, incompatible_program 반환).

06 · JSON Protocol & Diagnostics

Every se_buffer output function except the two opaque binary producers (se_program_artifact and se_session_save) emits a UTF-8 JSON envelope.

Standard Response Envelopes & 64-Bit Integer Encoding

// 1) Success envelope (status == SE_OK)
{ "ok": true, "result": { ... } }

// 2) Runtime / schema error envelope (SE_ERROR / SE_INVALID_ARGUMENT)
{
  "ok": false,
  "error": {
    "code": "pending_choice",
    "subject": "",
    "message": "Cannot update state while awaiting choice"
  }
}

// 3) Compilation / artifact rejection envelope (status == SE_COMPILE_ERROR)
{
  "ok": false,
  "error": { "code": "compile_error", "subject": "", "message": "Compilation failed" },
  "diagnostics": [
    {
      "severity": "error",
      "code": "unknown_turn",
      "subject": "greet",
      "message": "Route target turn does not exist: missing_turn"
    }
  ]
}

Canonical 64-Bit Integer Rule: To prevent IEEE-754 double rounding above ±2^53 in host JSON parsers (such as JavaScript or Godot’s built-in JSON.parse_string), all canonical integer outputs encode value as a base-10 string—for example, {"kind":"int","value":"9223372036854775807"}.

Session Entry, Step Execution Result & Choice Answers

  • Entry JSON (se_session_start): Pass {"turn":"record_opening"} for top-level turns, {"turn":"front","use":"a_visit"} for bound group uses, or {"turn":"window","group":"shop_flow","bindings":{"shop":"shop_b"}} for direct group entry.
  • Choice Answer JSON (answer_json): When phase == "awaiting_choice", pass {"request":"choice#1","option":"pay_fee","updates":[{"target":"last_roll","value":17}],"bindings":{}} (where updates and bindings are optional). Optional external state batch assignments in updates (writes to "internal" states fail with readonly_state) and optional bindings (defaulting to the active choice frame bindings) execute atomically before option.next routing and follow-up turn(s), prepending their deltas to the result’s changes array. Any validation, state update, math, or step-budget error rolls back the entire call completely to "awaiting_choice". Passing NULL or "{}" re-emits the pending prompt without mutating session state.
Step Result Field Type Description (result of se_session_advance / se_session_advance_to_yield)
phasestringResulting session phase: "ready", "awaiting_choice", "ended", or "idle".
executedstringID of the primary turn executed ("" if a choice exited directly out of an unbound open group).
kindstring"dialogue", "action", "update", "choice", "capture", or "end".
actor / textstringResolved entity ID and turn text ("" when absent).
payloadobjectOptional evaluated typed payload object on observable turns (key omitted when not authored).
request / choicesstring / arrayActive choice token and eligible options [{"id","text","payload"?}] when phase == "awaiting_choice".
changesarrayCommitted state deltas [{"state","before","after"}] (choice-answer updates are prepended ahead of subsequent update turn deltas in execution order).
snapshot_recordstringCaptured immutable snapshot record ID (e.g., "initial_ledger#1"), else "".
completed_choicestringTurn ID of the choice resolved by this call, else "".
next / exitstringNext scheduled turn ID and open-group exit name (when returning to "idle").

Program Inspection, State/Expression Queries & Opaque Binary Buffers

  • se_program_inspect: Emits a catalog object containing format, name, fingerprint, default_state_access, types, entities (with parents and transitive ancestors), states (with inherited and inherited_from), snapshots, groups, uses, and turns (with has_payload).
  • se_session_read / se_session_update / se_session_evaluate:
    • se_session_read(s, "[\"coins\"]", &out) → {"ok":true,"result":{"coins":{"kind":"int","value":"40"}}}
    • se_session_update(s, "[{\"target\":\"coins\",\"op\":\"add\",\"value\":10}]", NULL, &out) → returns ordered [{"state","before","after"}] deltas (rolls back entire batch on readonly_state, pending_choice, or type/overflow faults).
    • se_session_evaluate(s, "effective_fee", NULL, &out) → {"ok":true,"result":{"value":{...},"declared_type":""}}
  • Common Error Codes (error.code): Input & handle validation (invalid_argument, invalid_handle, invalid_json, invalid_schema, unsupported_format, resource_limit); phase, routing & access guards (invalid_phase, pending_choice, invalid_choice, no_choice, no_route, readonly_state, duplicate_target, step_budget_exceeded); math & range faults (division_by_zero, invalid_range when clamp has lo > hi—also caught at compile time for literal bounds, integer_overflow / overflow, numeric_precision / numeric_range, type_mismatch); and binary verification (invalid_artifact, invalid_checkpoint, incompatible_program).
  • Opaque Binary Artifacts & Checkpoints: Compiled story artifacts (se_program_artifact / se_program_load) and session checkpoints (se_session_save / se_session_load) are strictly opaque binary byte buffers (data + size). Never inspect, slice, or treat them as C strings; persist the exact size bytes and pass them back verbatim. On load, the engine verifies integrity and compatibility against the originating program, rejecting mismatches with invalid_artifact, invalid_checkpoint, or incompatible_program.

06 · JSON プロトコルと診断スキーマ

不透明バイナリを出力する se_program_artifact と se_session_save を除くすべての se_buffer 出力関数は、共通の UTF-8 JSON エンベロープ形式で応答します。

標準応答エンベロープと 64 ビット整数の表現

// 1) 成功エンベロープ (status == SE_OK)
{ "ok": true, "result": { ... } }

// 2) 実行時・スキーマエラーエンベロープ (SE_ERROR / SE_INVALID_ARGUMENT)
{
  "ok": false,
  "error": {
    "code": "pending_choice",
    "subject": "",
    "message": "Cannot update state while awaiting choice"
  }
}

// 3) コンパイル・アーティファクト検証失敗 (status == SE_COMPILE_ERROR)
{
  "ok": false,
  "error": { "code": "compile_error", "subject": "", "message": "Compilation failed" },
  "diagnostics": [
    {
      "severity": "error",
      "code": "unknown_turn",
      "subject": "greet",
      "message": "Route target turn does not exist: missing_turn"
    }
  ]
}

64 ビット整数の 10 進文字列エンコード: JavaScript や Godot の標準 JSON パーサなど、数値トークンを double として扱う環境での ±2^53 以上の丸め誤差を防ぐため、正規化出力の int 値は常に 10 進文字列(例:{"kind":"int","value":"9223372036854775807"})として出力されます。

開始設定・ステップ実行結果・選択応答スキーマ

  • 開始 JSON(se_session_start): 通常ターンは {"turn":"record_opening"}、グループ使用経由は {"turn":"front","use":"a_visit"}、グループへの直接進入は {"turn":"window","group":"shop_flow","bindings":{"shop":"shop_b"}} を指定します。
  • 選択応答 JSON(answer_json): phase == "awaiting_choice" のとき {"request":"choice#1","option":"pay_fee","updates":[{"target":"last_roll","value":17}],"bindings":{}}(updates と bindings は任意)を渡すと、任意の外部状態一括更新(updates、"internal" 状態への書き込みは readonly_state で拒否)および bindings(省略時は現在の選択フレームの束縛を使用)を option.next の経路解決と後続ターン実行の前にアトミックに適用し、その変更差分を結果の changes 配列の先頭に追加します。検証・更新・数値評価・ステップ予算超過のいずれかで失敗した場合は呼び出し全体が完全にロールバックされ "awaiting_choice" を維持します(NULL や "{}" を渡した場合は状態を変更せず現在の選択プロンプトを再返却します)。
ステップ結果フィールド 型 説明(se_session_advance / se_session_advance_to_yield の result)
phasestring遷移後のフェーズ:"ready", "awaiting_choice", "ended", "idle"。
executedstring実行された主要ターン ID(未束縛グループの出口へ直接抜けた場合は "")。
kindstring"dialogue", "action", "update", "choice", "capture", "end"。
actor / textstring解決されたエンティティ ID およびテキスト(未指定時は "")。
payloadobject可視ターンに設定された評価済みペイロード値オブジェクト(未設定時はキー省略)。
request / choicesstring / array"awaiting_choice" 時の要求 ID と有効な選択肢配列 [{"id","text","payload"?}]。
changesarray状態変更履歴 [{"state","before","after"}](選択応答の updates が先頭に記録され、後続の update ターン差分が順に累積)。
snapshot_recordstringcapture により生成されたレコード ID(例:"initial_ledger#1")。
completed_choicestring選択応答を完了した呼び出しでは対象選択ターンの ID、それ以外は ""。
next / exitstring次回実行予定のターン ID、およびグループ出口から "idle" に戻った際の出口名。

プログラム検査・状態操作・不透明バイナリ契約

  • se_program_inspect: format, name, fingerprint, default_state_access, types, entities(parents・ancestors 含む), states(inherited・inherited_from 含む), snapshots, groups, uses, turns(has_payload 含む)を返します。
  • se_session_read / se_session_update / se_session_evaluate:
    • se_session_read(s, "[\"coins\"]", &out) → {"ok":true,"result":{"coins":{"kind":"int","value":"40"}}}
    • se_session_update(s, "[{\"target\":\"coins\",\"op\":\"add\",\"value\":10}]", NULL, &out) → [{"state","before","after"}] を返却(readonly_state や pending_choice、型エラー時はバッチ全体をロールバック)。
    • se_session_evaluate(s, "effective_fee", NULL, &out) → {"ok":true,"result":{"value":{...},"declared_type":""}}
  • 主なエラーコード(error.code): 入力・ハンドル検証(invalid_argument, invalid_handle, invalid_json, invalid_schema, unsupported_format, resource_limit)、フェーズ・分岐・状態保護(invalid_phase, pending_choice, invalid_choice, no_choice, no_route, readonly_state, duplicate_target, step_budget_exceeded)、数値・範囲エラー(division_by_zero, invalid_range — clamp で lo > hi の場合に発生しリテラル境界はコンパイル診断でも検出, integer_overflow / overflow, numeric_precision / numeric_range, type_mismatch)、バイナリ整合性(invalid_artifact, invalid_checkpoint, incompatible_program)。
  • 不透明バイナリアーティファクトとチェックポイント: se_program_artifact / se_program_load のアーティファクトおよび se_session_save / se_session_load のチェックポイントは、内部構造が非公開の不透明(opaque)なバイナリバイトバッファ(data + size)です。バイト列を解析・改変せず必ず size バイト全体をそのまま保存・受け渡ししてください。ロード時にエンジンが元のプログラムとの整合性を検証し、不正データや別プログラムのチェックポイントは invalid_artifact・invalid_checkpoint・incompatible_program で拒否します。

07 · storyc CLI 레퍼런스

독립 Story Engine SDK 아카이브의 bin/storyc(Windows는 bin\storyc.exe)는 빌드 파이프라인 및 CI에서 스토리 소스를 정적 검증하고 불투명 바이너리 아티팩트(.story)로 컴파일하며 표준 입출력으로 시뮬레이션하는 명령줄 도구입니다.

Usage:
  storyc check <source.json>
  storyc compile <source.json> <output.story>
  storyc run <artifact.story> <entry.json> [seed]

서브커맨드별 입출력 동작

서브커맨드 표준 출력 (stdout) / 표준 에러 (stderr) 파일 및 대화형 동작
check <source.json> stdout에 단일 줄 진단 배열(무결 시 [], 경고/오류 시 [{"severity",...}]) 출력. 파일 열기·JSON 문법·스키마 오류는 stderr에 {"ok":false,"error":...} 출력. 아티팩트 파일을 생성하지 않고 정적 검증만 수행합니다. 경고만 있으면 종료 코드 0을 반환합니다.
compile <source.json> <output.story> check와 동일하게 진단 JSON 배열을 항상 stdout에 출력합니다. "severity":"error" 항목이 0개일 때만 불투명 바이너리 아티팩트를 <output.story>에 기록(덮어쓰기)합니다.
run <artifact.story> <entry.json> [seed] 매 턴 진행마다 {"ok":true,"result":{...}} 또는 런타임 오류 {"ok":false,"error":{...}}를 한 줄씩 stdout에 출력합니다. 컴파일된 .story 아티팩트를 로드(선택적 부호 없는 64비트 십진수 seed, 기본값 0)하고 첫 턴을 즉시 실행한 뒤, ended/idle 또는 stdin EOF까지 줄 단위로 진행합니다.

storyc run 실행 중 stdin에 빈 줄(또는 null, {}, "")을 보내면 일반 진행을 수행하고, awaiting_choice 단계에서는 {"request":"choice#1","option":"pay_fee"} 형태의 JSON 한 줄을 입력해 분기를 선택합니다. 대화형 루프 도중 잘못된 선택지를 입력해도 프로세스가 즉시 종료되지 않고 오류 JSON 줄을 출력한 뒤 다음 입력을 기다립니다.

종료 코드 (Exit Codes)

종료 코드 의미 발생 조건
0성공check / compile에 error 심각도 진단이 없거나, run이 ended / idle / EOF로 정상 종료된 경우
1컴파일·런타임·입출력 실패정적 컴파일 오류 발견, 손상된 아티팩트 로드, 파일 입출력 실패, JSON 문법/스키마 오류, 유효하지 않은 seed 문자열, 시작 단계 런타임 오류
2CLI 인자 오류알 수 없는 서브커맨드 또는 필수 위치 인자 개수 불일치 (stderr에 사용법 출력)

07 · storyc CLI Reference

Included at bin/storyc (bin\storyc.exe on Windows) in standalone Story Engine SDK archives, storyc validates authored JSON in CI, compiles opaque .story binary artifacts, and drives interactive sessions over standard streams.

Usage:
  storyc check <source.json>
  storyc compile <source.json> <output.story>
  storyc run <artifact.story> <entry.json> [seed]

Subcommand Stream & File Behavior

Subcommand stdout / stderr Contract Execution & File Semantics
check <source.json> Prints a single-line JSON diagnostic array to stdout ([] when clean). File I/O or JSON syntax/schema decode errors emit {"ok":false,"error":...} to stderr. Runs full static validation without writing an artifact. Warnings alone still exit with code 0.
compile <source.json> <output.story> Always prints the JSON diagnostic array to stdout. Writes (overwrites) the opaque binary artifact at <output.story> if and only if zero "severity":"error" diagnostics are emitted.
run <artifact.story> <entry.json> [seed] Emits one JSON envelope line per step ({"ok":true,"result":{...}} or {"ok":false,"error":{...}}) on stdout. Loads a compiled .story binary artifact (optional unsigned 64-bit base-10 seed, default 0), executes the initial turn immediately, and reads line-by-line from stdin until ended, idle, or EOF.

During storyc run, a blank line (or null, {}, "") advances a ready session; when phase == "awaiting_choice", send a single-line JSON answer such as {"request":"choice#1","option":"pay_fee"}. Recoverable runtime rejections (such as an invalid option ID) print an error JSON line to stdout and continue awaiting valid input.

Exit Codes

Exit Code Meaning Trigger Conditions
0SuccessZero error diagnostics in check / compile, or clean completion (ended / idle / EOF) in run.
1Validation / Runtime / I/O FailureCompile errors, invalid binary artifact, file I/O failure, malformed JSON/schema, out-of-range or non-numeric seed, or startup failure.
2CLI Usage ErrorUnknown subcommand or wrong positional argument count (prints usage to stderr).

07 · storyc CLI リファレンス

単体配布の Story Engine SDK アーカイブに含まれる bin/storyc(Windows は bin\storyc.exe)は、CI やビルドパイプラインでのソース検証、不透明バイナリアーティファクト(.story)の生成、および標準入出力での対話実行を行うコマンドラインツールです。

Usage:
  storyc check <source.json>
  storyc compile <source.json> <output.story>
  storyc run <artifact.story> <entry.json> [seed]

サブコマンド別の入出力仕様

サブコマンド stdout / stderr 出力 ファイルおよび実行動作
check <source.json> stdout に 1 行の診断 JSON 配列を出力(問題なしは [])。ファイル読込や JSON 構文/スキーマエラーは stderr に {"ok":false,"error":...} を出力。 ファイルを書き出さず静的検証のみを実行します。警告のみの場合は終了コード 0 となります。
compile <source.json> <output.story> check と同じく常に診断 JSON 配列を stdout に出力します。 "severity":"error" が 0 件のときのみ、不透明バイナリアーティファクトを <output.story> に書き出します。
run <artifact.story> <entry.json> [seed] ステップごとに {"ok":true,"result":{...}} またはエラー {"ok":false,"error":{...}} を 1 行ずつ stdout に出力します。 コンパイル済み .story ファイルをロードし(任意の符号なし 64 ビット 10 進数 seed、既定値 0)、最初のターンを即時実行後、ended・idle・EOF まで stdin を行単位で読み取ります。

storyc run の対話中は、空行(または null, {}, "")で通常進行し、awaiting_choice 状態では {"request":"choice#1","option":"pay_fee"} を 1 行で送信します。不正な選択肢などの実行時エラーが発生してもループは中断せず、エラー JSON を stdout に出力して次の入力を待機します。

終了コード(Exit Codes)

終了コード 意味 発生条件
0成功check / compile でエラー診断が 0 件、または run が ended / idle / EOF で正常終了した場合
1検証・実行・I/O 失敗コンパイルエラー、不正なアーティファクト、ファイル I/O 失敗、JSON 構文/スキーマ違反、不正な seed 指定、開始時のエラー
2CLI 引数エラー未知のサブコマンドまたは必須引数の個数不一致(stderr に使用法を出力)

08 · C++ / C# (Unity) / Godot 엔진 연동

모든 호스트 언어 어댑터는 동일한 C ABI(story_engine.h) 위에서 동작하며, 다음 4가지 공통 원칙을 따릅니다: ① 길이 기반 문자열의 내부 NUL 바이트 사전 차단(embedded_nul), ② 64비트 핸들의 명시적 소유권 해제(RAII / IDisposable / RefCounted), ③ 64비트 십진 문자열 정수 파싱, ④ 프레임 여유 시점에 호스트가 직접 호출하는 collect / collect_retired.

1. C++17 & Unreal Engine

C++17 호스트에서는 se_program, se_session, se_object, se_buffer를 이동 전용(Move-only) RAII 래퍼로 감싸 예외 안전성을 확보합니다. Unreal Engine 플러그인 모듈에서는 ThirdParty/StoryEngine/include/story_engine와 플랫폼별 라이브러리(Mac, Linux, Win64)를 Build.cs에 등록하며, 스토리 상태를 UObject GC에 종속시키지 않고 게임 서브시스템/컴포넌트 멤버 수명으로 관리합니다.

// C++17 RAII 패턴 예시 (story_engine.h 직접 래핑)
auto prog = story_engine::binding::Program::load(artifact_bytes.data(), artifact_bytes.size());
auto sess = prog.session(/*seed=*/42);
sess.start(R"({"turn":"record_opening"})");

// 내부 update/capture를 통과해 첫 가시 턴까지 진행
auto step_buf = sess.advance_to_yield(nullptr, /*max_steps=*/64);
std::string step_json = step_buf.text();

// 추론적 분기 미리보기용 O(1) 복제 및 불투명 체크포인트 저장
auto preview = sess.clone();
auto saved_checkpoint = sess.save(); // saved_checkpoint.data(), saved_checkpoint.size()

2. Unity 및 매니지드 C# (P/Invoke + SafeHandle)

Unity 및 .NET 호스트에서는 네이티브 바이너리(libstory_engine.dylib, libstory_engine.so, story_engine.dll)를 네이티브 플러그인 경로에 배치하고 CallingConvention.Cdecl로 P/Invoke합니다. 32비트 호스트에서도 핸들이 잘리지 않도록 네이티브 핸들은 IntPtr 대신 항상 ulong으로 유지하며, SafeHandle(DangerousAddRef / DangerousRelease)과 IDisposable을 함께 구현해 활성 호출 중 동시 Dispose 경쟁을 방지합니다.

using StoryEngine;

using var program = Program.Load(artifactBytes);
using var session = program.CreateSession(seed: 42UL);

using (session.Start("{\"turn\":\"record_opening\"}")) { }

using (Result step = session.AdvanceToYield(answer: null, maxSteps: 64))
{
    string json = step.Json; // UTF-8 JSON envelope
    // 선택 대기 시: session.AdvanceToYield("{\"request\":\"choice#1\",\"option\":\"pay\"}", 64)
}

// 씬 전환 등 호스트가 선택한 시점에 은퇴 세션 정리
using (Result gc = Collector.CollectRetired(budget: 128)) { }

3. Godot 4 (GDExtension / GDScript)

코어 공유 라이브러리의 C ABI 자체는 Godot 전용 심볼이 아니므로, godot-cpp 기반 GDExtension 래퍼(RefCounted 파생 StoryRuntime 및 StoryObject)를 통해 GDScript에 노출합니다. Godot 내장 JSON.parse_string은 JSON 숫자를 부동소수점으로 변환하므로, 정규화 int 값의 십진 문자열에 GDScript int(val["value"])를 호출해 64비트 부호 있는 정수 전 범위를 무손실로 복원하십시오.

var runtime = ClassDB.instantiate("StoryRuntime")
assert(JSON.parse_string(runtime.load_artifact(artifact_bytes))["ok"])
assert(JSON.parse_string(runtime.create_session(42))["ok"])
assert(JSON.parse_string(runtime.start('{"turn":"say"}'))["ok"])

var step = JSON.parse_string(runtime.advance())["result"]
if step["phase"] == "awaiting_choice":
    var answer = JSON.stringify({"request": step["request"], "option": step["choices"][0]["id"]})
    step = JSON.parse_string(runtime.advance(answer))["result"]

# 64비트 정수 무손실 읽기 (문자열 -> GDScript 64-bit int)
var coins_entry = JSON.parse_string(runtime.read('["coins"]'))["result"]["coins"]
var coins: int = int(coins_entry["value"])

runtime.close()
runtime.collect_retired(100)

08 · C++ / C# (Unity) / Godot Integration

All host language adapters layer cleanly atop the same C11 ABI (story_engine.h) and enforce four shared invariants: (1) reject embedded NUL bytes in length-aware host strings before crossing into C strings (embedded_nul), (2) manage 64-bit handles deterministically (RAII, IDisposable, or RefCounted), (3) parse canonical 64-bit integers from decimal strings, and (4) invoke collect / collect_retired explicitly at host-chosen frame boundaries.

1. C++17 & Unreal Engine

Wrap se_program, se_session, se_object, and se_buffer in move-only C++17 RAII owners. For Unreal Engine, place headers under ThirdParty/StoryEngine/include/story_engine and target binaries under ThirdParty/StoryEngine/lib/{Mac,Linux,Win64} in your module’s Build.cs. Store RAII owners inside subsystem or component members rather than wrapping individual story states as UObject instances.

// Move-only C++17 RAII usage over story_engine.h
auto prog = story_engine::binding::Program::load(artifact_bytes.data(), artifact_bytes.size());
auto sess = prog.session(/*seed=*/42);
sess.start(R"({"turn":"record_opening"})");

// Step past internal updates/captures to first observable turn
auto step_buf = sess.advance_to_yield(nullptr, /*max_steps=*/64);
std::string step_json = step_buf.text();

// Speculative branch clone and opaque binary checkpoint export
auto preview = sess.clone();
auto saved_checkpoint = sess.save(); // saved_checkpoint.data(), saved_checkpoint.size()

2. Unity & Managed C# (P/Invoke + SafeHandle)

Place the platform native binary (libstory_engine.dylib, libstory_engine.so, or story_engine.dll) in Unity’s Plugins folder and bind with [DllImport("story_engine", CallingConvention = CallingConvention.Cdecl)]. Keep native handles typed as ulong (never truncate into 32-bit IntPtr), validate UTF-16 surrogates and embedded NULs on managed strings, and pin calls with SafeHandle.DangerousAddRef / DangerousRelease alongside explicit Dispose().

using StoryEngine;

using var program = Program.Load(artifactBytes);
using var session = program.CreateSession(seed: 42UL);

using (session.Start("{\"turn\":\"record_opening\"}")) { }

using (Result step = session.AdvanceToYield(answer: null, maxSteps: 64))
{
    string json = step.Json; // UTF-8 JSON envelope
    // When awaiting choice: session.AdvanceToYield("{\"request\":\"choice#1\",\"option\":\"pay\"}", 64)
}

// Reclaim retired sessions during scene transitions or low-load frames
using (Result gc = Collector.CollectRetired(budget: 128)) { }

3. Godot 4 (GDExtension / GDScript)

Because the core C ABI is engine-agnostic, Godot 4 projects bind via a godot-cpp GDExtension exposing RefCounted wrappers (StoryRuntime and StoryObject). Godot’s built-in JSON.parse_string converts numeric tokens to floating-point, so pass canonical integer string digits directly to GDScript’s int(...) to preserve full signed 64-bit precision.

var runtime = ClassDB.instantiate("StoryRuntime")
assert(JSON.parse_string(runtime.load_artifact(artifact_bytes))["ok"])
assert(JSON.parse_string(runtime.create_session(42))["ok"])
assert(JSON.parse_string(runtime.start('{"turn":"say"}'))["ok"])

var step = JSON.parse_string(runtime.advance())["result"]
if step["phase"] == "awaiting_choice":
    var answer = JSON.stringify({"request": step["request"], "option": step["choices"][0]["id"]})
    step = JSON.parse_string(runtime.advance(answer))["result"]

# Lossless signed 64-bit integer conversion from canonical decimal string
var coins_entry = JSON.parse_string(runtime.read('["coins"]'))["result"]["coins"]
var coins: int = int(coins_entry["value"])

runtime.close()
runtime.collect_retired(100)

08 · C++ / C# (Unity) / Godot 統合

すべてのホスト言語アダプタは共通の C11 ABI(story_engine.h)上に構築され、4 つの原則を共有します:① 長さ付き文字列内の NUL バイト事前拒否(embedded_nul)、② 64 ビットハンドルの明示的な寿命管理(RAII / IDisposable / RefCounted)、③ 10 進文字列からの 64 ビット整数パース、④ ホスト側が任意のタイミングで呼び出す collect / collect_retired。

1. C++17 & Unreal Engine

se_program・se_session・se_object・se_buffer をムーブ専用の C++17 RAII クラスでラップします。Unreal Engine では ThirdParty/StoryEngine/include/story_engine と各 OS 用バイナリ(Mac, Linux, Win64)を Build.cs に登録し、ストーリー状態を UObject 化せずサブシステムやコンポーネントのメンバ寿命で管理します。

// story_engine.h 上のムーブ専用 C++17 RAII パターン
auto prog = story_engine::binding::Program::load(artifact_bytes.data(), artifact_bytes.size());
auto sess = prog.session(/*seed=*/42);
sess.start(R"({"turn":"record_opening"})");

// 内部の update / capture を通過して最初の可視ターンまで進行
auto step_buf = sess.advance_to_yield(nullptr, /*max_steps=*/64);
std::string step_json = step_buf.text();

// 分岐プレビュー用の複製と不透明チェックポイントの保存
auto preview = sess.clone();
auto saved_checkpoint = sess.save(); // saved_checkpoint.data(), saved_checkpoint.size()

2. Unity およびマネージド C#(P/Invoke + SafeHandle)

ネイティブプラグイン(libstory_engine.dylib, libstory_engine.so, story_engine.dll)を配置し、CallingConvention.Cdecl で P/Invoke します。32 ビット環境での切り捨てを防ぐためネイティブ ID は常に ulong で保持し、SafeHandle(DangerousAddRef / DangerousRelease)と IDisposable を併用して呼び出し中の並行 Dispose 競合を防ぎます。

using StoryEngine;

using var program = Program.Load(artifactBytes);
using var session = program.CreateSession(seed: 42UL);

using (session.Start("{\"turn\":\"record_opening\"}")) { }

using (Result step = session.AdvanceToYield(answer: null, maxSteps: 64))
{
    string json = step.Json; // UTF-8 JSON エンベロープ
    // 選択待ち時: session.AdvanceToYield("{\"request\":\"choice#1\",\"option\":\"pay\"}", 64)
}

// シーン遷移時など任意のタイミングで退役セッションを回収
using (Result gc = Collector.CollectRetired(budget: 128)) { }

3. Godot 4(GDExtension / GDScript)

コアの C ABI 自体は GDExtension シンボルではないため、godot-cpp ベースの RefCounted ラッパー(StoryRuntime および StoryObject)を経由して GDScript から呼び出します。Godot 標準の JSON.parse_string は数値を浮動小数点に変換するため、正規化 int の 10 進文字列に int(...) を適用することで符号付き 64 ビット精度を維持できます。

var runtime = ClassDB.instantiate("StoryRuntime")
assert(JSON.parse_string(runtime.load_artifact(artifact_bytes))["ok"])
assert(JSON.parse_string(runtime.create_session(42))["ok"])
assert(JSON.parse_string(runtime.start('{"turn":"say"}'))["ok"])

var step = JSON.parse_string(runtime.advance())["result"]
if step["phase"] == "awaiting_choice":
    var answer = JSON.stringify({"request": step["request"], "option": step["choices"][0]["id"]})
    step = JSON.parse_string(runtime.advance(answer))["result"]

# 正規化 10 進文字列から GDScript の 64 ビット int へ無損失変換
var coins_entry = JSON.parse_string(runtime.read('["coins"]'))["result"]["coins"]
var coins: int = int(coins_entry["value"])

runtime.close()
runtime.collect_retired(100)

09 · Story Editor 및 원격 MCP 레퍼런스

602 Story Editor v0.1.2(Story Engine v1.2.0 네이티브 런타임 번들 내장)는 외부 Python/Node.js 설치 없이 실행되는 독립 실행형 시각 그래프 편집·시뮬레이션·에이전트 서버입니다. 웹 편집 인터페이스는 가로 1440px 이상의 데스크톱 브라우저 환경을 기준으로 설계되었습니다.

1. 계정 초기화 및 HTTPS 리버스 프록시 기동

계정 인증 DB(--account-database)와 프로젝트 DB(--database)는 아카이브 외부의 서로 다른 비-심볼릭 링크 절대 경로에 배치해야 합니다. 백엔드 리스너는 루프백(127.0.0.1 또는 ::1)에서만 동작하므로 앞단에 TLS를 종료하는 Caddy 또는 Nginx를 배치하고 --trust-https-proxy와 함께 실행합니다.

# 1) 초기 관리자 계정 생성 (24시간 유효 임시 비밀번호 발급, 첫 로그인 시 /change-password 필수)
./story-editor account \
  --database /var/lib/story-editor/auth.sqlite3 \
  --username writer \
  --owner-email writer@example.com

# 2) 루프백 백엔드 기동 (외부 공개 HTTPS 오리진 필수)
./story-editor serve \
  --public-url https://story.example \
  --account-database /var/lib/story-editor/auth.sqlite3 \
  --database /var/lib/story-editor/editor.sqlite3 \
  --host 127.0.0.1 \
  --port 8781 \
  --trust-https-proxy
# Caddyfile 예시 (Host 및 X-Forwarded-Proto 자동 전달 + 즉시 스트림 플러시)
story.example {
    reverse_proxy 127.0.0.1:8781
}

# Nginx 핵심 location 설정 (Host 유지 및 에이전트 NDJSON 스트리밍 버퍼링 해제 필수)
location / {
    proxy_pass http://127.0.0.1:8781;
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_buffering off;
}

2. 시각적 그래프 작성, 중간 턴 시뮬레이션 및 story-agent

  • 그래프 작성 및 중간 장면 점프 시뮬레이션: 6종 주 작업 턴과 6종 보조 계산·분기 노드(계산 노드의 min(a, b), max(a, b), clamp(value, lo, hi) 지원; lo > hi이면 invalid_range 거부), 재사용 묶음(Group)을 캔버스에서 편집하고 내장 네이티브 엔진으로 즉시 컴파일합니다. 시뮬레이션 드롭다운의 지정 턴에서 중간 시작으로 임의의 턴(그룹 결속 포함)과 초기 상태값을 지정해 즉시 검증할 수 있으며, 선택지 진행 시 answer.updates를 통해 외부 상태 갱신과 후속 분기 실행을 원자적으로 함께 수행할 수 있습니다(실패 시 선택 대기 상태 유지).
  • 내장 story-agent 협업 모드: 세션별로 변경안을 미리보기로 스테이징해 검토 후 반영하는 approval 모드와 즉시 반영하는 full_access 모드를 전환할 수 있습니다. 외부 모델 연결은 STORY_AGENT_PROVIDER=openai + STORY_AGENT_API_KEY(전 플랫폼 공통, Windows 필수) 또는 POSIX 전용 STORY_AGENT_PROVIDER=openai-codex + STORY_AGENT_AUTH_DIR(./agent/story-agent --login)로 구성합니다.

3. 원격 MCP 엔드포인트 (POST /mcp) 및 6자리 해시 동시성 제어

에디터 하단 에이전트 창의 MCP 설정 모달(또는 POST /api/turn/mcp/tokens)에서 발급받은 Bearer 토큰과 세션 ID로 외부 에이전트를 연결합니다. 모든 POST /mcp 요청(최대 본문 256 KiB, 토큰당 분당 120회 제한)에는 Authorization: Bearer <token>과 Story-Editor-Session: <session_id> 헤더가 필수입니다. Modern Stateless 프로토콜(2026-07-28: server/discover → tools/list → tools/call)과 Legacy(2025-11-25: initialize)를 모두 지원합니다.

MCP 도구 주요 입력 인자 동작 및 6자리 16진수 해시 계약
inspect scope: "view" | "projects" | "project" | "manuscript", projectId, collection, ids, query, related 열린 화면, 프로젝트 목록, 단일 프로젝트 요약, 또는 원고 객체와 연결 포트를 조회하며 6자리 소문자 16진수 metadataHash 및 각 객체의 6자리 hash를 반환합니다.
edit scope: "project" (create | select | update) 또는 scope: "manuscript" (operations[]: create | update | delete | set) update / delete / set 시 대상의 현재 6자리 expectedHash를 검증합니다. 권한에 따라 status: "applied" 또는 승인 대기 "pending"을 반환합니다.
analyze kind: "validation", 선택적 projectId 내장 네이티브 Story Engine 컴파일러로 현재 원고(또는 대기 중인 제안 기준 상태)를 정적 검증하여 success와 diagnostics[]를 반환합니다.

낙관적 동시성 충돌 복구 (project_conflict): 다른 사용자나 세션이 먼저 원고를 수정해 6자리 해시(예: "5900a9")가 변경된 경우, edit는 JSON-RPC 치명 오류 대신 복구 가능한 도구 결과 오류(result.isError: true, structuredContent.error.code: "project_conflict", requiresInspect: true)를 반환합니다. 클라이언트는 inspect를 다시 호출해 최신 6자리 hash / metadataHash를 확인한 뒤 패치를 재적용하면 됩니다.

# Modern Stateless MCP (2026-07-28) tools/call 호출 예시
curl -sS -X POST https://story.example/mcp \
  -H "Authorization: Bearer $STORY_EDITOR_MCP_TOKEN" \
  -H "Story-Editor-Session: $STORY_EDITOR_SESSION_ID" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: inspect" \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "inspect",
      "arguments": { "scope": "manuscript", "collection": "turns", "limit": 16 },
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

09 · Story Editor & Remote MCP

602 Story Editor v0.1.2 (bundling the native Story Engine v1.2.0 runtime) is a standalone visual graph authoring, runtime simulation, and agent collaboration server requiring zero pre-installed Python or Node.js runtimes. Its web workspace targets desktop viewports at 1440px width or wider.

1. Account Initialization & HTTPS Reverse Proxy Deployment

Store --account-database and --database at distinct, non-symlink absolute paths outside the extracted archive directory. Because story-editor serve binds strictly to loopback HTTP (127.0.0.1 or ::1), place a TLS-terminating reverse proxy (Caddy or Nginx) in front and pass --trust-https-proxy.

# 1) Initialize operator account (24h temporary password; forces /change-password on first login)
./story-editor account \
  --database /var/lib/story-editor/auth.sqlite3 \
  --username writer \
  --owner-email writer@example.com

# 2) Launch loopback server behind your public HTTPS origin
./story-editor serve \
  --public-url https://story.example \
  --account-database /var/lib/story-editor/auth.sqlite3 \
  --database /var/lib/story-editor/editor.sqlite3 \
  --host 127.0.0.1 \
  --port 8781 \
  --trust-https-proxy
# Caddyfile (automatically preserves Host, sets X-Forwarded-Proto, and flushes streams)
story.example {
    reverse_proxy 127.0.0.1:8781
}

# Nginx location block (preserve Host and disable proxy_buffering for NDJSON agent streams)
location / {
    proxy_pass http://127.0.0.1:8781;
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_buffering off;
}

2. Visual Graph Authoring, Arbitrary-Turn Simulation & story-agent

  • Graph Editing & Scene-Jump Simulation: Author nominal types, entity hierarchies, snapshots, 6 primary turn kinds, 6 pure/routing auxiliary nodes (including calculation node operators min(a, b), max(a, b), and clamp(value, lo, hi), rejecting lo > hi with invalid_range), and reusable parameterized groups on the canvas, backed by live native compilation. Use Start from Selected Turn in the Simulation dropdown to jump straight into any mid-story scene with custom or randomized test states, and pass atomic external state mutations via answer.updates during choice advances (rolling back to awaiting choice on failure).
  • story-agent Proposal Workflow: Switch per session between approval mode (stages diff proposals for human review before commit) and full_access mode (applies edits immediately). Configure external LLM providers via STORY_AGENT_PROVIDER=openai + STORY_AGENT_API_KEY (all platforms; required on Windows) or POSIX OAuth via STORY_AGENT_PROVIDER=openai-codex + STORY_AGENT_AUTH_DIR (./agent/story-agent --login).

3. Remote MCP (POST /mcp) & 6-Hex Optimistic Concurrency

Mint scoped Bearer tokens and session IDs from the in-app MCP modal (or POST /api/turn/mcp/tokens using a logged-in __Host-story_session browser cookie). Every POST /mcp request (256 KiB max body; 120 requests / 60s per token) requires both Authorization: Bearer <token> and Story-Editor-Session: <session_id>. Both Modern Stateless MCP (2026-07-28: server/discover → tools/list → tools/call) and Legacy MCP (2025-11-25: initialize) are supported.

MCP Tool Key Arguments Capabilities & 6-Hex Concurrency Contract
inspect scope: "view" | "projects" | "project" | "manuscript", projectId, collection, ids, query, related Queries active workspace state, project list, project counts, or manuscript nodes/edges, returning 6-character lowercase hex metadataHash and per-object hash values.
edit scope: "project" (create | select | update) or scope: "manuscript" (operations[]: create | update | delete | set) Verifies 6-hex expectedHash on every update, delete, and set operation; commits (status: "applied") or stages a proposal (status: "pending") depending on session/token permission mode.
analyze kind: "validation", optional projectId Runs the bundled native Story Engine compiler against the manuscript, returning success and structured diagnostics[].

Optimistic Concurrency Recovery (project_conflict): When a 6-hex expectedHash (such as "5900a9") no longer matches server state due to concurrent edits, edit returns HTTP 200 with a recoverable tool domain error (result.isError: true, structuredContent.error.code: "project_conflict", requiresInspect: true). Re-run inspect to fetch the updated 6-hex hash and retry.

# Modern Stateless MCP (2026-07-28) tools/call example
curl -sS -X POST https://story.example/mcp \
  -H "Authorization: Bearer $STORY_EDITOR_MCP_TOKEN" \
  -H "Story-Editor-Session: $STORY_EDITOR_SESSION_ID" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: inspect" \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "inspect",
      "arguments": { "scope": "manuscript", "collection": "turns", "limit": 16 },
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

09 · Story Editor とリモート MCP

602 Story Editor v0.1.2(ネイティブ Story Engine v1.2.0 ランタイムを同梱)は、Python や Node.js の事前インストール不要で動作するスタンドアロン型のビジュアルグラフ編集・シミュレーション・エージェント連携サーバーです。Web UI は横幅 1440px 以上のデスクトップブラウザ環境を前提としています。

1. アカウント初期化と HTTPS リバースプロキシ構成

認証 DB(--account-database)とプロジェクト DB(--database)には、アーカイブ外の互いに異なる非シンボリックリンクの絶対パスを指定してください。バックエンドはループバック(127.0.0.1 または ::1)のみで待ち受けるため、前段に TLS 終端を行う Caddy または Nginx を配置し --trust-https-proxy を付与して起動します。

# 1) 管理者アカウント初期化(一時パスワードは24時間有効、初回ログイン時に /change-password 必須)
./story-editor account \
  --database /var/lib/story-editor/auth.sqlite3 \
  --username writer \
  --owner-email writer@example.com

# 2) ループバック HTTP サーバー起動(公開 URL は https:// 必須)
./story-editor serve \
  --public-url https://story.example \
  --account-database /var/lib/story-editor/auth.sqlite3 \
  --database /var/lib/story-editor/editor.sqlite3 \
  --host 127.0.0.1 \
  --port 8781 \
  --trust-https-proxy
# Caddyfile 設定例(Host と X-Forwarded-Proto の自動転送・即時ストリームフラッシュ)
story.example {
    reverse_proxy 127.0.0.1:8781
}

# Nginx location 設定例(Host 維持と NDJSON ストリーム向け proxy_buffering 無効化が必須)
location / {
    proxy_pass http://127.0.0.1:8781;
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_buffering off;
}

2. ビジュアルグラフ編集・途中ターンからのシミュレーション・story-agent

  • グラフ編集と途中シーンジャンプ検証: 6 種の主要ターン・6 種の補助ノード(計算ノードの min(a, b)・max(a, b)・clamp(value, lo, hi) に対応し、lo > hi は invalid_range で拒否)・再利用グループをキャンバス上で編集し、内蔵ネイティブエンジンで即座にコンパイルできます。シミュレーションメニューの指定ターンから途中開始を使うと任意のターンと初期状態値で即座にテストでき、選択肢進行時には answer.updates により外部状態更新と後続実行をアトミックに処理できます(失敗時は選択待ちを維持)。
  • 内蔵 story-agent 提案ワークフロー: 変更案をプレビューして承認後に反映する approval モードと即時反映する full_access モードを切り替え可能です。外部 AI プロバイダは STORY_AGENT_PROVIDER=openai + STORY_AGENT_API_KEY(全 OS 共通・Windows 必須)、または POSIX 環境での STORY_AGENT_PROVIDER=openai-codex + STORY_AGENT_AUTH_DIR(./agent/story-agent --login)で設定します。

3. リモート MCP(POST /mcp)と 6 桁ハッシュ楽観的ロック

エディタ内の MCP 設定モーダル(または POST /api/turn/mcp/tokens)で発行した Bearer トークンとセッション ID を用いて外部クライアントから接続します。すべての POST /mcp リクエスト(最大 256 KiB、トークンあたり毎分 120 回制限)には Authorization: Bearer <token> と Story-Editor-Session: <session_id> ヘッダが必須です。Modern Stateless フロー(2026-07-28:server/discover → tools/list → tools/call)および Legacy(2025-11-25:initialize)の両方に対応しています。

MCP ツール 主な入力引数 機能と 6 桁 16 進ハッシュ契約
inspect scope: "view" | "projects" | "project" | "manuscript", projectId, collection, ids, query, related 画面状態・プロジェクト一覧・単一プロジェクト要約・原稿オブジェクトと接続ポートを取得し、6 桁小文字 16 進の metadataHash および各オブジェクトの hash を返します。
edit scope: "project"(create | select | update)または scope: "manuscript"(operations[]: create | update | delete | set) update / delete / set 実行時に現在の 6 桁 expectedHash を照合します。権限モードに応じ status: "applied" または承認待ち "pending" を返します。
analyze kind: "validation"、任意の projectId 内蔵ネイティブ Story Engine コンパイラで原稿を静的検証し、success と diagnostics[] を返します。

ハッシュ競合からの復旧(project_conflict): 並行編集により 6 桁の expectedHash(例:"5900a9")が最新状態と一致しない場合、edit は回復可能なドメインエラー(result.isError: true、structuredContent.error.code: "project_conflict"、requiresInspect: true)を返します。再度 inspect を呼んで最新の 6 桁ハッシュを取得してから再試行してください。

# Modern Stateless MCP (2026-07-28) tools/call リクエスト例
curl -sS -X POST https://story.example/mcp \
  -H "Authorization: Bearer $STORY_EDITOR_MCP_TOKEN" \
  -H "Story-Editor-Session: $STORY_EDITOR_SESSION_ID" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: inspect" \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "inspect",
      "arguments": { "scope": "manuscript", "collection": "turns", "limit": 16 },
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

10 · 릴리스 노트 및 라이선스

릴리스 하이라이트

  • 602 Story Engine SDK v1.2.0 (C ABI v1 / story-engine/turn-source/1):
    • 선택 응답(answer_json)의 선택적 updates 및 bindings 지원: option.next 경로 및 후속 턴 실행 전에 외부 상태를 원자적으로 일괄 갱신하고 결과의 changes 배열 앞쪽에 기록하며, 실패 시 호출 전체를 롤백해 awaiting_choice 상태를 유지합니다(internal 상태 쓰기는 readonly_state 거부).
    • 순수 수치 표현식 연산자 min(a, b), max(a, b), clamp(x, lo, hi) 추가(총 31개 연산자; clamp의 역순 경계 lo > hi는 invalid_range 거부).
    • 추론 실행 및 롤백을 위한 세션 복제(se_session_clone) 및 원본 프로그램 지문과 대조 검증되는 불투명 바이너리 체크포인트(se_session_save, se_session_load, 최대 16 MiB).
    • default_state_access 및 states[].access("external" | "internal") 기반 외부 변경 보호(readonly_state), entities[].parents 컴파일 타임 슬롯 상속 및 그룹 엔티티 매개변수 isa 서브타입 검증.
    • 가시 턴 및 선택지의 타입 검증 구조화 페이로드(payload / payload_expr), 내부 비가시 턴 예산 기반 연속 진행(se_session_advance_to_yield, step_budget_exceeded), 프로그램 카탈로그 리플렉션(se_program_inspect), SE_COMPILE_ERROR 시 ok: false 오류 봉투 일원화.
  • 602 Story Editor v0.1.2:
    • Story Engine v1.2.0 네이티브 공유 라이브러리 및 story_engine.h 번들 통합.
    • 계산 노드의 min(a, b), max(a, b), clamp(value, lo, hi) 지원(lo > hi이면 invalid_range 오류) 및 선택지 진행 시 answer.updates 원자적 외부 상태 갱신 지원.
    • 외부 런타임 설치가 필요 없는 독립 실행형 story-editor 서버 및 agent/story-agent 패키지(linux-x64, windows-x64, macos-arm64).
    • 시각적 턴·묶음 그래프 편집기, 중간 턴 시작 시뮬레이터, 6자리 해시 동시성 보호 기반 원격 MCP(POST /mcp, 2026-07-28 / 2025-11-25).

플랫폼 및 운영 시 유의사항 요약

  • 에디터 아키텍처 범위: Engine SDK는 macos-x64를 포함한 4개 플랫폼을 지원하지만, Story Editor v0.1.2는 linux-x64, windows-x64, macos-arm64 3개 플랫폼으로만 배포됩니다.
  • 코드 서명 및 HTTPS 프록시: 현재 릴리스 바이너리에는 상용 코드 서명·공증이 적용되어 있지 않으며, story-editor serve 운영 시 반드시 앞단에 HTTPS 리버스 프록시가 필요합니다.
  • Windows 외부 AI 공급자 인증: Windows 빌드에서 외부 AI 공급자를 사용할 때는 OAuth 브라우저 로그인(--login) 대신 STORY_AGENT_PROVIDER=openai + STORY_AGENT_API_KEY 모드를 사용하십시오(기본 편집·컴파일·시뮬레이션·원격 MCP는 API 키 없이 동작).

CHOAM 602 라이선스 요약 및 서드파티 오픈소스 고지

602 Story Engine SDK — CHOAM Community License, Version 1.0: 602 Story Engine SDK(story_engine.h 및 네이티브 엔진 라이브러리·storyc)에는 CHOAM Community License 1.0이 적용됩니다(Copyright (c) 2026 CHOAM Co., Ltd.). 초기 도입 비용이나 사전 등록 없이 자사 제품(게임·애플리케이션·서비스)에 포함하여 배포할 수 있으며, 개별 제품 및 역년 분기별로 순매출 첫 10,000,000원(KRW)까지는 로열티가 없고 1,000만 원을 초과한 금액에 대해서만 1% 로열티(분기 종료 후 45일 이내 자진 신고)가 발생합니다. 소프트웨어를 단독 배포하거나 경쟁 범용 엔진·SDK·도구의 일부로 배포하는 것은 금지되며, 배포 시 라이선스 파일과 저작권 고지를 유지해야 합니다. 약관 본문은 영문 원문(/602/download 전문 수록)이 기준 정본이며 국문·일문은 참고 번역입니다(준거법: 대한민국법, 전속관할: 서울중앙지방법원).

602 Story Editor — Apache License 2.0 (Apache-2.0): 602 Story Editor 자체에는 Apache License 2.0이 적용됩니다(단, 독립 실행형 에디터 배포 아카이브 내부에 번들된 네이티브 602 Story Engine 공유 라이브러리 런타임에는 CHOAM Community License 1.0이 적용됩니다).

문의 창구: 라이선스·상업 이용(로열티 신고 포함)은 mino@choam.studio, 버그 제보·기능 제안·기술 문의는 cayde@choam.studio로 연락해 주십시오.

구성 요소 라이선스 포함 패키지 및 고지 경로
RapidJSON MIT / BSD-style Notices Story Engine SDK 및 Story Editor 네이티브 엔진 (licenses/rapidjson.txt)
Node.js Runtime MIT License & Bundled Runtime Notices Story Editor 독립 실행형 에이전트 워커 (licenses/)
pi (pi-agent-core) MIT License Story Editor 내장 에이전트 오케스트레이션 런타임 (licenses/)
Python / FastAPI / React OSS Ecosystem PSF, MIT, BSD, Apache-2.0 Notices Story Editor 백엔드(Python, FastAPI, Starlette, Uvicorn, Pydantic) 및 웹 프론트엔드(React) 서드파티 고지 전문 (licenses/)

10 · Release Notes & Licenses

Release Highlights

  • 602 Story Engine SDK v1.2.0 (C ABI v1 / story-engine/turn-source/1):
    • Atomic external state batch updates (updates) and bindings on choice answers (se_session_advance / se_session_advance_to_yield), applied before option.next routing, prepended to changes, and rolled back completely to awaiting_choice on error (readonly_state rejects internal state writes).
    • Pure numeric expression operators min(a, b), max(a, b), and clamp(x, lo, hi) (31 total operators; inverted clamp bounds lo > hi rejected with invalid_range).
    • Session cloning (se_session_clone) and opaque binary session checkpoints (se_session_save, se_session_load, up to 16 MiB) verified by the engine against the originating program.
    • State mutability control via default_state_access and states[].access ("external" | "internal"), compile-time entity state slot inheritance (entities[].parents), and subtype checking on group entity parameters (isa).
    • Type-checked structured payload / payload_expr attachments on observable turns and choice options, bounded multi-turn stepping (se_session_advance_to_yield, step_budget_exceeded), program catalog inspection (se_program_inspect), and unified ok: false envelopes on SE_COMPILE_ERROR.
  • 602 Story Editor v0.1.2:
    • Bundles Story Engine v1.2.0 native shared libraries and story_engine.h.
    • Supports min(a, b), max(a, b), and clamp(value, lo, hi) in calculation nodes (invalid_range when lo > hi) and atomic external state updates via answer.updates on choice advances.
    • Self-contained story-editor server and agent/story-agent binaries across linux-x64, windows-x64, and macos-arm64 with zero host Python/Node.js setup.
    • Visual turn/group graph workspace, arbitrary-turn scene jump simulator, and Remote MCP (POST /mcp, 2026-07-28 & 2025-11-25) guarded by 6-hex optimistic concurrency hashes.

Platform & Operational Caveats

  • Platform Matrix: Story Engine SDK ships for 4 platforms (including macos-x64), whereas Story Editor v0.1.2 ships for 3 platforms (linux-x64, windows-x64, macos-arm64).
  • Code Signing & HTTPS Proxy: Binaries are currently unsigned / ad-hoc signed without Apple notarization or Windows Authenticode; story-editor serve requires a TLS-terminating HTTPS reverse proxy in front of its loopback listener.
  • Windows External AI Provider Auth: On Windows, configure external LLM providers with STORY_AGENT_PROVIDER=openai and STORY_AGENT_API_KEY (OAuth --login is POSIX-only; core editing, native compilation, simulation, and Remote MCP require no AI credentials).

CHOAM 602 Licensing Summary & Third-Party Open-Source Notices

602 Story Engine SDK — CHOAM Community License, Version 1.0: 602 Story Engine SDK (story_engine.h, native engine libraries, and storyc) is published under the CHOAM Community License 1.0 (Copyright (c) 2026 CHOAM Co., Ltd.). There is no upfront fee and no registration to use and distribute the Software as part of your Products (games, applications, or services). For each Product and each calendar Quarter, no royalty is due on the first KRW 10,000,000 of Net Revenue; Net Revenue above KRW 10,000,000 in that Quarter carries a 1% self-reported royalty on the amount above KRW 10,000,000 (reported within 45 days after the Quarter). Standalone redistribution or bundling inside a competing general-purpose engine, SDK, or tool is prohibited, and distributed copies must retain the license file and copyright notice. The English license text (full verbatim text on /602/download and inside each SDK archive) is the authoritative version, with Korean and Japanese provided as reference translations; governing law is the Republic of Korea (Seoul Central District Court).

602 Story Editor — Apache License 2.0 (Apache-2.0): 602 Story Editor itself is licensed under Apache License 2.0 (the embedded native 602 Story Engine runtime library bundled inside standalone Story Editor archives remains governed by CHOAM Community License 1.0).

Contacts: Licensing & commercial use (including royalty self-reporting): mino@choam.studio · Bug reports, feature requests & technical inquiries: cayde@choam.studio.

Component License Distribution Scope & Notice Location
RapidJSON MIT / BSD-style Notices Story Engine SDK & Story Editor native engine library (licenses/rapidjson.txt)
Node.js Runtime MIT License & Bundled Runtime Notices Story Editor standalone agent worker binary (licenses/)
pi (pi-agent-core) MIT License Story Editor built-in agent runtime (licenses/)
Python / FastAPI / React OSS Ecosystem PSF, MIT, BSD, Apache-2.0 Notices Story Editor server runtime (Python, FastAPI, Starlette, Uvicorn, Pydantic) & web frontend (React) notices in licenses/

10 · リリースノートとライセンス

リリースハイライト

  • 602 Story Engine SDK v1.2.0(C ABI v1 / story-engine/turn-source/1):
    • 選択応答(answer_json)における任意の updates・bindings 対応:option.next の経路解決および後続ターン実行の前に外部状態をアトミックに一括更新し、結果の changes 配列の先頭に記録します(internal 状態への書き込みは readonly_state で拒否され、エラー時は呼び出し全体をロールバックして awaiting_choice を維持)。
    • 純粋数値演算子 min(a, b)・max(a, b)・clamp(x, lo, hi) を追加(全 31 演算子、逆転した clamp 境界 lo > hi は invalid_range で拒否)。
    • セッション複製(se_session_clone)および元のプログラムと照合検証される不透明バイナリチェックポイント(se_session_save / se_session_load、最大 16 MiB)。
    • default_state_access および states[].access("external" | "internal")による外部書き換え保護(readonly_state)、entities[].parents によるコンパイル時スロット継承とグループ引数 isa による派生型検証。
    • 可視ターンおよび選択肢への型付き構造化ペイロード(payload / payload_expr)、内部ターンの予算付き一括進行(se_session_advance_to_yield、step_budget_exceeded)、カタログ検査(se_program_inspect)、および SE_COMPILE_ERROR 時の ok: false エンベロープ統一。
  • 602 Story Editor v0.1.2:
    • Story Engine v1.2.0 ネイティブ共有ライブラリおよび story_engine.h を統合。
    • 計算ノードでの min(a, b)・max(a, b)・clamp(value, lo, hi) 対応(lo > hi は invalid_range エラー)および選択肢進行時の answer.updates によるアトミックな外部状態更新に対応。
    • Python / Node.js 導入不要のスタンドアロン story-editor サーバーおよび agent/story-agent(linux-x64, windows-x64, macos-arm64)。
    • ビジュアルグラフ編集、任意ターンからの途中開始シミュレータ、6 桁ハッシュ楽観的ロックを備えたリモート MCP(POST /mcp、2026-07-28 / 2025-11-25)。

プラットフォームおよび運用上の留意事項

  • 配布プラットフォームの範囲: Engine SDK は macos-x64 を含む 4 プラットフォーム向けですが、Story Editor v0.1.2 は linux-x64・windows-x64・macos-arm64 の 3 プラットフォーム配布となります。
  • コード署名と HTTPS プロキシ: 現在のバイナリには商用コード署名および公証が適用されていません。また story-editor serve の前段には HTTPS リバースプロキシが必須です。
  • Windows における外部 AI プロバイダ認証: Windows 環境で外部 AI プロバイダを利用する場合は OAuth(--login)ではなく STORY_AGENT_PROVIDER=openai + STORY_AGENT_API_KEY を使用してください(基本編集・コンパイル・シミュレーション・リモート MCP は API キー不要で動作します)。

CHOAM 602 ライセンス概要およびサードパーティ OSS 表記

602 Story Engine SDK — CHOAM Community License, Version 1.0: 602 Story Engine SDK(story_engine.h、ネイティブ共有ライブラリ、および storyc)には CHOAM Community License 1.0(Copyright (c) 2026 CHOAM Co., Ltd.)が適用されます。初期費用や事前登録なしで自身の製品(ゲーム・アプリケーション・サービス)の一部として組み込み・配布でき、各製品および各暦四半期ごとに純売上の最初の 10,000,000 韓国ウォン(KRW)まではロイヤリティが発生せず、1,000 万ウォンを超えた金額に対してのみ 1% の自己申告ロイヤリティ(四半期終了後 45 日以内に申告)が発生します。単体での再配布や競合する汎用エンジン・SDK・ツールの一部としての配布は禁止されており、配布物にはライセンスファイルと著作権表示を保持する必要があります。規約本文は英語版(/602/download および各アーカイブに全文収録)を正文とし、韓国語訳・日本語訳は参考訳として提供されます(準拠法:大韓民国法、専属管轄:ソウル中央地方裁判所)。

602 Story Editor — Apache License 2.0(Apache-2.0): 602 Story Editor 本体には Apache License 2.0 が適用されます(スタンドアロン配布アーカイブ内に同梱されるネイティブ 602 Story Engine ランタイムライブラリには CHOAM Community License 1.0 が適用されます)。

お問い合わせ窓口: ライセンス・商用利用(ロイヤリティ申告を含む)は mino@choam.studio、バグ報告・機能要望・技術的なお問い合わせは cayde@choam.studio までご連絡ください。

コンポーネント ライセンス 同梱対象・表記ファイル
RapidJSON MIT / BSD-style Notices Story Engine SDK および Story Editor ネイティブエンジン(licenses/rapidjson.txt)
Node.js Runtime MIT License & Bundled Runtime Notices Story Editor スタンドアロンエージェントワーカー(licenses/)
pi (pi-agent-core) MIT License Story Editor 内蔵エージェントランタイム(licenses/)
Python / FastAPI / React OSS Ecosystem PSF, MIT, BSD, Apache-2.0 Notices Story Editor バックエンド(Python, FastAPI, Starlette, Uvicorn, Pydantic)および Web フロントエンド(React)の各種 OSS 表記(licenses/)