Files
wip-reference/contents/2.1-object-tree-schema.md

207 lines
14 KiB
Markdown

# 2.1. Object tree / Schema
## Objectの公開情報
Objectは、Worldspace上の対象をprotocol-level projectionとして公開するものである。Clientはその公開情報を使って対象を発見し、理解し、操作する。
少なくとも次の情報がObjectの公開情報に含まれる。
- **name** — 親Objectの下でのpath segment。
- **description** — Objectが何を表すかを説明する情報。
- **interfaces** — Objectで利用できるOperationを定義するInterface参照のordered set。
- **ref** — 同じClient-facing Worldspace内で複数の観測またはエントリが同じObjectを表すことを示すoptionalなmarker。
- **validator** — HostがObjectの現在状態へ付与する任意のopaque value。
`interfaces`のmembershipにはset semanticsを適用し、同じInterface参照を重複して含めてはならない。空の集合も許可する。列挙順はOperation解決には影響しないが、HostがInterfaceを立項した順序としてUI表示に使用できる。
各情報の具体的な型、必須性、resource limitは[3. Protocol](3-protocol.md)で定義する。
Objectの本文、状態、domain metadata等のApplication dataは、Interfaceに宣言されたOperationを通して取得または変更する。Objectへ共通のproperty bagとして格納しない。
`description`も、長い本文やdomain entityの完全な表現を格納する場所ではない。探索時に対象を理解するための短い公開情報である。
### 同じ`ref`を持つObject
同じClient-facing Worldspace内で同じ`ref`を持つObjectには、一つの共有された現在状態がある。同じObjectを異なるpathへ配置しても、path固有の`name`、`description`、Interface集合を返してはならない。
- 同じ`ref`と同じvalidator valueを持つObjectは、validatorが存在する場合、`name`、`description`、Interface集合を含むObject representationが一致しなければならない。
- 同じ`ref`と異なるvalidatorを持つObjectは、同一Objectの異なる状態として扱う。
- Hostは異なるpathから同じ`ref`を観測した場合も、一つの現在状態を一貫して返す。
- Clientは最後に成功した観測を、その`ref`を持つObjectの現在状態として扱う。
異なる親の下へ配置した場合も、同じObjectの`name`は共通である。Pathは親から各Objectの`name`を辿って構成するので、異なる親を経由すれば、同じObjectが異なるpathに現れ得る。
## Interface参照
Objectは0個以上のInterfaceを参照する。Interface Descriptorは、Objectに対して実行可能なOperationを宣言するものであり、一つのcapability namespaceを定義する。
Operation名は、一つのInterface Descriptor内で一意であればよい。異なるInterface間では同じOperation名を使用できる。Operation callではscopeとlocal nameからなるInterface参照を明示するため、Protocol上の解決は曖昧にならない。人間・AI向けのqualified表記は[Text Viewの参照表記](4.2-text-view.md#参照表記)に従う。
Interfaceには次の定義を含める。
- Operation名
- Operationの説明
- Parameter名と型
- Return type
- Named typeとdocumentation
Interface実装者が実装コード上でInterfaceを宣言すると、Libraryはその宣言からInterface Descriptorを生成する。ClientはObjectの各Interface参照でDescriptorを取得し、検証、cache、表示、Operation callのvalidationに利用する。
同じInterfaceを持つObjectは、同じDescriptor参照を共有できる。
## Interface scope
Objectをpath `P`で公開する場合、Interface参照のscopeは`P`自身か、その祖先でなければならない。Self-scopeも許可するので、rootから`P`までの各pathが候補となる。祖先かどうかはpath segmentの完全一致で判定し、単純な文字列prefixでは判定しない。本仕様のInterface scopeに関する「祖先制約」にはself-scopeも含む。
例えば、`/github/my-rep/issues`で公開できるのは、scopeが`/`、`/github`、`/github/my-rep`、`/github/my-rep/issues`であるInterfaceである。Scopeが`/gitlab`、`/github/other-rep`、`/github/my-rep/issues/123`であるInterfaceは使用できない。文字列の先頭が一致しても、`/github`は`/github-extra`の祖先にはならない。Root Objectはscope `/`のInterfaceを公開できる。
この制約の基準は配置pathであり、Objectがindexableかどうかではない。非indexableなObjectを直接観測した場合にも同じ規則を適用する。Scope Objectを先に観測する必要はなく、そのObject自身が同じInterfaceを公開する必要もない。
Scopeが適合していても、`Object.interfaces`への明示的なmembershipは別に必要である。祖先scopeのInterfaceは自動継承せず、scopeをauthorizationの証明にも使わない。
Scopeは、そのpathにあるObjectに紐づく。Objectとは独立して存続するnamespaceではないため、scope Objectがそのpathから消えると、そのscopeのInterfaceの公開も終了する。消えたObjectの配下を、親Objectの存在しない孤立したsubtreeとして公開し続けない。同じpathへ別のObjectを公開し直すことはできる。
Scope Objectに`ref`があれば、ClientはそのObjectとの紐付きを確認できる。一方、`ref`はoptionalなので、存在しない場合には、同じpathでの差し替えをidentityによって追跡できない。この公開形態も許可されており、Hostに`ref`の生成を強制したり、Clientが代替identityを推測したりしない。参照の有効期間、呼出時の確認、移動とcacheの扱いは[Operation and Interface](2.2-operation-and-interface.md#scope-objectと有効期間)および[Protocol](3-protocol.md#scope-objectの紐付き)に従う。
## Object / エントリ / path
ObjectをTree上の位置として扱うときは、そのObjectをエントリと呼ぶ。WIPでは、Objectとエントリを別のdomain entityとしては定義しない。
### `name`
Objectの`name`は、親Objectの下でそのObjectを識別するpath segmentである。表示用labelとは区別し、次の条件を適用する。
- Root Objectの`name`は空文字列とする。
- 非root Objectの`name`は空であってはならない。
- `name`に`/`を含めてはならない。
- `name`全体が`.`または`..`であってはならない。
- `name`はUnicode stringとする。
- Unicode normalizationは行わない。
- Nameの比較はUnicode code point sequenceの完全一致で行う。
- 同じ親の直接のchildrenは、互いに異なる`name`を持たなければならない。
このようにpath segmentとして使う`name`を、人間向けの自由なtitleとして扱わない。探索時の短い説明は`description`で公開し、domain上のtitle等はOperationからApplication dataとして取得する。
### Pathの構成
Rootのpathは`/`とする。非root Objectのpathは、rootからそのObjectまでの各`name`を`/`で連結する。
```text
root.name = ""
root.path = "/"
projects.name = "projects"
projects.path = "/projects"
example.name = "example"
example.path = "/projects/example"
```
Pathには空segment、`.`、`..`を含めず、root以外のpathの末尾には`/`を付けない。Nameに`/`を許可しないため、Core Modelではsegment escapeを定義しない。Transport BindingでPathをURL componentへ格納するときのURL encodingは、WIP Pathの構成とは別に扱う。
また、非root pathの最後のsegmentは、取得したObjectの`name`と一致しなければならない。
```text
observe("/projects/example", depth = 0)
-> ObjectObservation.object.name == "example"
```
同じObjectを複数のpathへ配置するときは、各配置で同じ`ref`と`name`を持ち、異なる親を経由して異なるpathに現れる。Renameすると、同じObjectを参照するすべての配置とdescendantのpathが変化する。このため、pathを恒久的なObject identityとして使用しない。
`ref`、path、Interface参照は、それぞれ次の役割を持つ。
| 値 | 役割 |
| --- | --- |
| path / `entry` | RootからObjectの`name`を辿って構成するnavigation coordinate |
| `ref` | 複数の観測にまたがる任意のObject identity |
| Interface参照 | Objectで利用可能なcapability definition |
Operation value内でWorldspace上の位置を返す場合は、Descriptorで`entry`型として宣言する。通常のstringを、内容だけからpathや`ref`と推測しない。
## Tree
Objectは子Objectを持つことができる。このparent / child関係は、Worldspaceにおける**所有または内包**を表す。
- Filesystem: Directory / File
- GitHub: Repository / Issue
- Database: Database / Table
- RAG: Index / Document
これに対し、関連、依存、参照等の非所有関係をTreeの親子関係として濫用しない。そのような関係はOperationのresultとして`entry`を返し、Clientが必要に応じて移動できるようにする。
### `children`の意味
`children`は、指定pathでObjectを観測した時点のWorldspace上のprojectionである。Application dataには含めない。
一つのObjectを複数pathへ配置した場合、見えるchildrenは配置ごとに異なり得る。そのため、childrenは`ref`が示すObject identityの一部とはみなさない。
Protocolでは、`Object`自体にchildrenとpathを含めない。`observe` responseの`ObjectObservation`で、Objectとoptionalなchildren observationをまとめる。ValidatorはObjectに含め、各nodeのpathはrequest pathを起点にObjectの`name`を辿って導出する。
`ObjectObservation`は、指定depthまでの観測結果を表すresponse shapeである。このwrapperによって、Objectとは別のdomain entityを導入するわけではない。
Nodeの`children` fieldを省略した場合、そのnodeのchildrenは観測していない。Fieldが存在する場合、そのarrayには、その主体へ公開された直接のindexable childrenをすべて含める。空arrayは、観測時点でindexable childが存在しなかったことを表す。
## Objectの可視性
Client-facing Worldspaceは、Hostがその主体に認識させるObjectだけで構成する。Objectを公開すると、そのpathとObjectの公開情報を主体が認識できる。
反対に、主体に認識させないObjectは、そのWorldspaceには存在しないものとして扱う。
- `observe`では、そのObjectと、そのObjectだけを経由して到達するsubtreeをresponseから省略する。
- 省略されたObjectやchild数を示すredaction markerは返さない。
- 非公開pathを起点とする`observe`には、Host内部での存在理由にかかわらず`NotFound`を返す。Object観測に対する`PermissionDenied`は使用しない。
Objectを公開する場合でも、特定Interfaceを集合から除外したり、公開したInterfaceから一部のOperationを除外したりできる。公開されたOperationの実行を認可時に拒否することもできる。Operationの可視性と`PermissionDenied`の使い分けは[2.2. Operation and Interface](2.2-operation-and-interface.md)で定義する。
`description`に含めるのは、その主体へ公開できる説明だけである。安全な説明を公開できない場合は、optionalな`description`を省略できる。ただし、Protocolはfield-level redaction markerを定義しない。本文やdomain dataを部分的に開示する場合は、各Operationのresult schemaとdomain semanticsで表現する。
## `observe`
`observe(path, depth)`は、指定pathのObjectを起点に、indexable child edgeを指定depthまで辿った範囲を一つの観測として取得する。
```text
observe(path, depth) -> ObjectObservation
```
起点Objectのdepthは`0`である。`depth = 0`では起点Objectだけを返し、そのnodeの`children` fieldを省略する。成功responseには、requested depthまでの公開されたindexable childrenを完全に含め、それより深いObjectは含めない。
- Nodeのdepthがrequestの`depth`より小さい場合、`children` fieldを必ず含める。
- 観測した直接のindexable childが存在しない場合、`children`は空arrayとする。
- Nodeのdepthがrequestの`depth`と等しい場合、`children` fieldを省略する。省略はemptyを意味しない。
- 同じparent nodeのchildrenは、互いに異なる`name`を持つ。
- Clientはrequest pathとchildrenの`name`から各nodeのpathを導出する。
各ObjectはHostが発行したvalidatorを持ち得る。Clientは、導出したpathとvalidatorから条件付きOperationのtargetを構築できる。
Responseを部分的な成功として返すための`limit`や`truncated`は定義しない。Requested depthまでの完全な観測をresource budget内で構築できない場合、Hostはsuccess responseを返さず、`ResourceLimitExceeded`を返す。
## Indexable children
Tree上のchildrenとして自然に列挙できるのは、**indexableなObject**だけである。
- 件数がboundedまたは現実的な範囲で列挙できる。
- 比較的安価に取得できる。
- 所有または内包関係として自然に理解できる。
全件列挙が高コストまたは不自然な集合は、Treeへ直接展開しない。その集合を扱うObjectのOperationで検索、filter、paginationを行い、関連する`entry`をresultとして返す。
例えば、大規模なIssue collectionは次の形で公開できる。参照には、scope `/`を含む短縮表記を使う。
```text
/issues
interfaces: ["/::IssueCollection"]
operation: /::IssueCollection#query(filter, cursor, limit) -> IssueQueryResult
```
そのquery resultでは、itemsを`entry`のarrayとして返す。
```text
IssueQueryResult.items: [entry]
```
## 複数の取得
複数のObjectやInterface Descriptorを取得する場合でも、各`observe`と`fetch_interface`は独立したCore interactionである。Clientは必要なrequestを個別に並行実行できる。Transport実装ではconnection reuseやmultiplexingを利用できる。
WIPは、複数のinteractionを包む共通request envelope、自動的な全量取得、暗黙の再帰展開、Clientが要求していない先読みを定義しない。