14 KiB
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で定義する。
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の参照表記に従う。
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およびProtocolに従う。
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を/で連結する。
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と一致しなければならない。
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で定義する。
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まで辿った範囲を一つの観測として取得する。
observe(path, depth) -> ObjectObservation
起点Objectのdepthは0である。depth = 0では起点Objectだけを返し、そのnodeのchildren fieldを省略する。成功responseには、requested depthまでの公開されたindexable childrenを完全に含め、それより深いObjectは含めない。
- Nodeのdepthがrequestの
depthより小さい場合、childrenfieldを必ず含める。 - 観測した直接のindexable childが存在しない場合、
childrenは空arrayとする。 - Nodeのdepthがrequestの
depthと等しい場合、childrenfieldを省略する。省略は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 /を含む短縮表記を使う。
/issues
interfaces: ["/::IssueCollection"]
operation: /::IssueCollection#query(filter, cursor, limit) -> IssueQueryResult
そのquery resultでは、itemsをentryのarrayとして返す。
IssueQueryResult.items: [entry]
複数の取得
複数のObjectやInterface Descriptorを取得する場合でも、各observeとfetch_interfaceは独立したCore interactionである。Clientは必要なrequestを個別に並行実行できる。Transport実装ではconnection reuseやmultiplexingを利用できる。
WIPは、複数のinteractionを包む共通request envelope、自動的な全量取得、暗黙の再帰展開、Clientが要求していない先読みを定義しない。