docs: define operation IDL and entry discovery

This commit is contained in:
2026-09-08 12:11:52 +09:00
parent 138fda8ea6
commit 1520852238
9 changed files with 435 additions and 45 deletions
+19 -8
View File
@@ -8,17 +8,28 @@
- Object / エントリ公開情報の具体的なwire schema。
- `name`とpath segmentの関係。
- entry pathのcanonicalization規則。
- `description`の長さや必須性をprotocol上でどこまで規定するか。
- Linkの標準表現。
- Operation Result内でエントリ / エントリツリー / Linkをどう統一的に表現するか。
## 2.2. Operation
## 2.2. Operation / WIP IDL
対象: [2.2. Operation](../2.2-operation.md)
対象: [2.2. Operation](../2.2-operation.md)、[WIP IDL](wip-idl.md)
- input / outputに使う型システム
- WIP IDL sourceのcanonicalization、digest algorithm、参照方法
- WIP IDLのversion間における互換性規則。
- Sourceにdescriptionやdocumentation commentを含めるか。
- String length、numeric range、pattern等のconstraintを導入するか。
- Recursive named typeを許可するか。
- `i64``u64``bigint``decimal`等を追加するか。
- `description`の必須性と長さ。
- optional / union / enum / record等の表現。
- Object / Linkを値として返す場合の標準表現。
- Guardの具体的schema。
- error型をOperation outputに含めるか、Protocol errorとして分離するか。
- error型をOperation outputに含めるか、Protocol errorとして分離するか。
## WIP over HTTPS
対象: [WIP over HTTPS](wip-over-https.md)
- 複数の`fetch`をまとめるbatch request / responseのwire schema。
- Invoke時に`included`を要求する方法と、Hostが自動同梱できる条件。
- `included`のresponse size上限とtruncation表現。
- `included`と個別の`fetch`で観測整合性情報をどう共有するか。
+281
View File
@@ -0,0 +1,281 @@
# WIP IDL
WIP IDLは、Operationのinput / outputを記述するための小さなinterface definition languageである。
WIP IDLのsourceそのものをschemaのcanonicalな交換形式とする。HostとClientの間でJSON SchemaやIDLを変換したJSON ASTを交換することは前提としない。Clientはsourceをparseして内部表現を構築し、必要に応じてAI Tool schemaやGUI formへ投影する。
この文書では、WIP IDLの初期文法と型を定義する。
## 設計方針
- Operation境界の型だけを表現する。
- 文法を小さく保ち、一般的なschema validation languageにはしない。
- Operationのinputは名前付きfieldを持つrecordとする。
- Wire上の値表現はTransport Bindingが定める。
- JSON SchemaはWIPの交換形式にせず、必要なClientが生成する。
- Worldspaceのエントリは、そのpathを表す`entry`型として扱う。
## Source
WIP IDL sourceはUTF-8 textとする。ASCII space、tab、LFによる空白は、tokenを分離する場合を除いて意味を持たない。
`//`から行末まではcommentとし、parserは無視する。
IdentifierはASCIIの英字または`_`で始まり、以降にASCIIの英数字または`_`を含められる。
```text
identifier = (ALPHA | "_") { ALPHA | DIGIT | "_" };
```
Keyword、primitive type名、Worldspace固有型名はidentifierとして使用できない。
Named typeにはUpperCamelCase、Operation、field、enum case、union caseにはsnake_caseを使用する。
## Grammar
以下のEBNFは空白とcommentを省略して示す。
```ebnf
document = { declaration } ;
declaration = type-declaration | operation-declaration ;
type-declaration = "type", type-name, "=", type-definition, ";" ;
type-definition = type-expression | enum-type | union-type ;
operation-declaration =
"operation", identifier, "{",
"input", ":", type-expression, ";",
"output", ":", type-expression, ";",
"}" ;
type-expression =
primitive-type
| worldspace-type
| type-name
| record-type
| list-type ;
record-type =
"{", [ record-field, { ",", record-field }, [ "," ] ], "}" ;
record-field = identifier, [ "?" ], ":", type-expression ;
list-type = "[", type-expression, "]" ;
enum-type =
"enum", "{", identifier, { ",", identifier }, [ "," ], "}" ;
union-type =
"union", "{", union-case, { ",", union-case }, [ "," ], "}" ;
union-case = identifier, [ "(", type-expression, ")" ] ;
primitive-type =
"unit"
| "boolean"
| "integer"
| "number"
| "string"
| "bytes"
| "json" ;
worldspace-type = "entry" ;
type-name = identifier ;
```
同じdocument内でtype名またはOperation名を重複して宣言してはならない。Named typeは同じdocument内で宣言されたtypeを参照する。
## Primitive types
| Type | 意味 | WIP over HTTPSでの表現 | 制約 |
| --- | --- | --- | --- |
| `unit` | 値を持たない | JSON `null` | Optional fieldの省略とは区別する |
| `boolean` | 真偽値 | JSON boolean | — |
| `integer` | 符号付き整数 | 小数部を持たないJSON number | `-9007199254740991`以上、`9007199254740991`以下 |
| `number` | IEEE 754 binary64値 | JSON number | NaN、positive infinity、negative infinityは不可 |
| `string` | Unicode string | JSON string | — |
| `bytes` | 任意のoctet列 | RFC 4648 Section 4のbase64を格納したJSON string | paddingを含む |
| `json` | opaqueな任意のJSON value | JSON value | Clientは内部構造をschemaとして解釈しない |
`integer`の範囲は、異なる言語のClient間で正確に交換できる範囲に制限している。この範囲を超える整数は`integer`としてencodeしない。`i64``u64``bigint`等の追加型とlosslessなwire表現は将来の拡張として扱う。
`json`は外部APIの応答など構造を事前に固定できない値のためのescape hatchであり、通常のOperation interfaceではより具体的な型を優先する。
## Composite types
| Type | 構文 | 意味 | WIP over HTTPSでの表現 |
| --- | --- | --- | --- |
| Record | `{ field: Type }` | 名前付きfieldの集合 | JSON object |
| List | `[Type]` | 同じ型の順序付き値 | JSON array |
| Named type | `type Name = Type;` | 型への名前付け | 参照先と同じ表現 |
| Enum | `enum { first, second }` | payloadを持たないcaseの集合 | case名のJSON string |
| Union | `union { case(Type), empty }` | discriminatorを持つcaseの集合 | `$case`を持つJSON object |
### Record
Recordは名前付きfieldの集合である。
```wip
type Query = {
text: string,
limit?: integer,
};
```
`?`を持たないfieldはrequired、`?`を持つfieldはoptionalである。Optionalはrecord fieldにのみ適用し、一般化しない。
WIP over HTTPSではrecordをJSON objectとしてencodeする。Optional fieldに値がない場合はfield自体を省略する。FieldをJSON `null`にすることは省略と同じ意味ではなく、そのfieldの型が`unit`または`json`として`null`を許す場合に限る。
宣言されていないfieldをrecordに含めてはならない。
### List
Listは同じ型の0個以上の順序付き値を表す。
```wip
[entry]
```
WIP over HTTPSではJSON arrayとしてencodeする。
### Named type
`type`宣言によって型へ名前を付けられる。
```wip
type IssueList = [entry];
```
Aliasを参照する値のwire表現は、参照先の型と同じである。
### Enum
Enumはpayloadを持たないcaseの集合である。
```wip
type Relation = enum {
parent,
sibling,
related,
};
```
WIP over HTTPSではcase名をJSON stringとしてencodeする。
```json
"sibling"
```
### Union
Unionはdiscriminatorを持つcaseの集合である。Caseは0個または1個のpayloadを持つ。
```wip
type LookupResult = union {
found(entry),
not_found,
ambiguous([entry]),
};
```
WIP over HTTPSでは`$case` discriminatorを持つJSON objectとしてencodeする。Payloadを持つcaseは`value` fieldに値を格納する。
```json
{
"$case": "found",
"value": "/items/123"
}
```
Payloadを持たないcaseには`value`を含めない。
```json
{
"$case": "not_found"
}
```
## Worldspace type
| Type | 意味 | WIP over HTTPSでの表現 |
| --- | --- | --- |
| `entry` | 同じWorldspace内のエントリを指すcanonical absolute path | JSON string |
```json
"/items/123"
```
Wire上では通常の`string`と同じ表現だが、WIP IDL上で`entry`と宣言された位置にある値だけをClientがエントリとして認識する。通常の`string`やopaqueな`json`に含まれるpathらしい文字列を、Clientがエントリとして推測してはならない。
Operation Resultから`entry`を発見したClientは、そのpathを一時的に利用し、必要に応じて`fetch`または`fetch_tree`する。複数のpathを効率的に取得するためのbatchingや、取得結果をinvoke responseへ同梱する最適化は、`entry`型のwire表現とは分離する。
Operationが返したpathは、Operationの実行対象の子に限定されない。親、兄弟、関連Objectなど、Worldspace内の任意のエントリを指してよい。Operationの実行対象と返されたpathの間に、暗黙のTree edgeや意味的関係を推論してはならない。
`fetch`が返すエントリ公開情報と、`fetch_tree`が返すindexableなtreeはCore Protocolのresponseであり、WIP IDLの値型としては定義しない。
## Operation declarations
Operationはinputとoutputをそれぞれ一つ宣言する。
```wip
type Relation = enum {
parent,
sibling,
related,
};
operation get_related_item {
input: {
relation: Relation,
limit?: integer,
};
output: {
items: [entry],
next_cursor?: string,
};
}
```
Operationのinputは、直接またはNamed typeを介してrecordへ解決されなければならない。位置引数は定義しない。
引数を持たないOperationは空のrecordをinputにする。
```wip
operation refresh {
input: {};
output: unit;
}
```
Outputには任意のtype expressionまたはNamed typeを使用できる。ただし、将来fieldを追加する可能性があるOperationではrecord outputを推奨する。
Operationのname、description、Guardなど、input / output型以外のinterface metadataとの対応付けは[Operation schema](../2.2-operation.md)で定義する。
## Schema exchange
HostはOperation interfaceとともに、次のschema情報をClientへ提供する。
| 項目 | 内容 |
| --- | --- |
| Language identifier | 初期versionは`wip-idl/1` |
| Source | WIP IDL source、またはsourceを取得するための参照 |
| Digest | Cacheと同一性確認に利用するdigest |
交換されるschemaの正本はWIP IDL sourceである。HostはJSON SchemaやIDL ASTを併記する必要はない。
ClientはIDLを内部ASTへparseし、必要に応じてAI provider用JSON Schema、GUI form、言語固有の型などを生成できる。
Sourceのcanonicalization規則、digest algorithm、参照方法の詳細はTransport Bindingで定義する。
## 未解決事項
- Sourceにdescriptionやdocumentation commentを含めるか。
- String length、numeric range、pattern等のconstraintを導入するか。
- Recursive named typeを許可するか。
- `i64``u64``bigint``decimal`等を追加するか。
- Sourceのcanonicalizationとdigestの厳密な計算方法。
- `entry`が参照するpathのcanonicalization規則。
+73
View File
@@ -24,6 +24,79 @@ HTTPSを利用することで、TLS、認証、Proxy、Gateway、Observability
具体的なHTTP method、endpoint、content type、error mapping、認証方式、streaming表現などは、このbinding仕様側で定義する。
## WIP IDLの取得
Operationのinput / output schemaは[WIP IDL](wip-idl.md)のsourceとして交換する。HostはIDLのlanguage identifier、sourceまたはsourceを取得するための参照、およびcacheと同一性確認に利用できるdigestを公開する。
HTTPS response全体をJSON envelopeにする場合、IDL sourceをJSON stringとして格納してよい。ただし、これはIDLをJSON ASTへ変換するものではない。BindingはIDL sourceを専用resourceおよびcontent typeで直接取得する方法を定義してもよい。
## Operation valueのJSON encoding
WIP over HTTPSでは、`call_operation`のinputとoutputをWIP IDLに従うJSON valueとしてencodeする。JSONはHTTPS binding上の値表現であり、WIP Coreの型定義形式ではない。
`input`は常に名前付きfieldを持つJSON objectとする。位置引数は用いず、引数を持たないOperationには空のobjectを渡す。
概念的なrequest bodyは次の形になる。
```json
{
"target": "/items/current",
"operation": "get_related_item",
"input": {
"relation": "sibling",
"limit": 10
}
}
```
IDL型からJSONへのmappingはbindingで一意に定める。少なくとも次の規則を持つ。
- `boolean``string``number`、record、listは対応するJSON valueで表す。
- optionalなrecord fieldはfieldの省略で表し、`null`とは区別する。
- unit形式のenumはstring、payloadを持つunionは明示的なdiscriminatorを持つobjectで表す。
- `bytes`はbase64でencodeしたstringとして表す。
- 通常のJSON numberで表す`integer`は相互運用可能な安全範囲に制限する。それを超える整数型を設ける場合はdecimal string等のlosslessな表現を定義する。
- WIP IDLの`entry`は、同じWorldspace内のcanonical absolute pathをJSON stringとして表す。
ClientはWIP IDLに従ってvalueをdecodeし、IDL上で`entry`と宣言された位置にある文字列だけをエントリとして認識する。通常の`string`や任意JSON値に含まれるpathらしい文字列を、エントリとして推測してはならない。
## Entry取得のbatching
Operation Resultに複数のentry pathが含まれる場合、Clientはそれらを抽出して重複を除去し、必要なpathを取得できる。
Core Protocol上では各`fetch`を独立した観測として扱う。WIP over HTTPSは、複数の`fetch`を一つのHTTP requestへまとめ、個別に実行した場合と同じ結果を返すbatch表現を定義してよい。Batchingは通信上の最適化であり、Core Protocolへ別の意味を追加しない。
## Invoke responseへの同梱
Hostは追加のround tripを避けるため、Operation Resultに含まれるentry pathの公開情報を、invoke responseの`included` sidecarへ同梱してよい。
```json
{
"output": {
"items": [
"/items/123",
"/items/456"
]
},
"included": {
"/items/123": {
"type": "item",
"description": "First item",
"operations": []
},
"/items/456": {
"type": "item",
"description": "Second item",
"operations": []
}
}
}
```
`included`のkeyはOperation Resultに現れるcanonical entry path、valueは同じpathに対する通常の`fetch`結果と同じ観測とする。`included`はOperationのtyped outputには含まれず、Operationのoutput schemaを変更しない。
Clientは`included`をfetch済みの観測として利用しても、無視して改めて取得してもよい。Clientが同梱を要求する方法、Hostが自動的に同梱できる条件、response sizeの上限はbindingの詳細として定める。
## 非目標
WIP over HTTPSはHost内部のAPIやデータソース接続方式を規定しない。また、WIP Core ProtocolそのものをHTTP依存のモデルへ変えることも目的としない。