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

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より小さい場合、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 /を含む短縮表記を使う。

/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が要求していない先読みを定義しない。