From c4fdff5b6b1cdd9e63e92882bd9642b8d46a811d Mon Sep 17 00:00:00 2001 From: Hare Date: Wed, 7 Oct 2026 03:28:34 +0900 Subject: [PATCH] docs: draft scoped interface references and composition --- 2.2-operation.md | 2 + draft/dynamic-worldspace-projection.md | 2 +- draft/interface-scope.md | 163 +++++++++++++++++++++++++ draft/unresolved-issues.md | 2 +- 4 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 draft/interface-scope.md diff --git a/2.2-operation.md b/2.2-operation.md index 54b3fe1..2854dce 100644 --- a/2.2-operation.md +++ b/2.2-operation.md @@ -12,6 +12,8 @@ Interface実装者は、RustのtraitやTypeScriptのdefinition API等を使っ Interface Descriptorは人間が手書きするschema fileではない。Hostが`fetch_interface`で公開し、Clientが参照単位でcacheするprotocol dataである。 +Interface参照へWorldspaceのpath scopeを導入する変更案は、[Scoped Interface References(草案)](draft/interface-scope.md)で扱う。現行Protocolのopaque参照を置き換えるための草案であり、本章の確定仕様へはまだ適用しない。 + ## Interface Descriptor 一つのInterface Descriptorは一つのInterfaceを定義し、少なくとも次を保持する。 diff --git a/draft/dynamic-worldspace-projection.md b/draft/dynamic-worldspace-projection.md index 8761bf8..4b6f7fd 100644 --- a/draft/dynamic-worldspace-projection.md +++ b/draft/dynamic-worldspace-projection.md @@ -262,7 +262,7 @@ Composition後も、同じ主体へ公開されたWorldspace全体で`ref`とInt - 同じ有効なInterface参照とvalidatorの組を異なるDescriptorへ対応付けない。 - Validator比較がProvider routingの変更によって別Objectへ適用されない。 -具体的なnamespaceおよび発行方式は決定しない。 +Interface参照のnamespace案は[Scoped Interface References(草案)](interface-scope.md)で扱う。Worldspaceの絶対pathをscopeとし、Objectの祖先制約と、合成後の公開位置に合わせたHostの参照変換を検討する。Objectの`ref`の具体的な発行方式は引き続き未決定とする。 ただし、mount API、path routing、Provider間のparent / child関係、同名entry conflict、cross-provider Operation、transaction boundaryは未決定である。ProtocolはHost内部のcomposition方式を公開しないため、Host APIでこれらをどこまで標準化するかを別途検討する。 diff --git a/draft/interface-scope.md b/draft/interface-scope.md new file mode 100644 index 0000000..572db5a --- /dev/null +++ b/draft/interface-scope.md @@ -0,0 +1,163 @@ +# Scoped Interface References(草案) + +## 位置付け + +Interface参照にWorldspace上のpathをscopeとして持たせ、独立したProviderやWorldspaceが定義したInterfaceを、Hostによる合成後も衝突なく公開するためのProtocol変更案である。 + +この草案では、次を設計方針とする。 + +- Interface参照は、絶対pathのscopeと、そのscope内のlocal nameから構成する。 +- Objectが公開できるInterfaceは、Objectの配置pathの祖先をscopeに持つものに制限する。 +- 絶対pathは、Clientに対してHostが公開するWorldspaceのrootを基準とする。 +- ProxyやWorldspace合成によって公開位置が変わる場合、Clientへ見せるpathとInterface参照を移動後の配置へ変換するのはHostの責務とする。 + +現行の[3. Protocol](../3-protocol.md)ではInterface参照全体をopaque stringとして扱っている。本案はその一部に構造と意味を与えるものであり、単なる表示上の修飾ではない。Canonical文書へ採用するまでは草案として扱い、未確定の構文や境界条件を末尾にまとめる。 + +## Interface参照 + +表記案は次のとおりとする。 + +```text +:: + +/github::issue.read +/gitlab::issue.read +/github/my-rep::issue.admin +/::common.read +``` + +- `scope-path`はHostが公開するWorldspace内のcanonical absolute path。 +- `local-name`はそのscope内でInterfaceを識別する名前。例の`issue.read`に含まれる`.`はObjectの階層やOperation名との区切りを意味しない。 +- Interfaceはscopeとlocal nameの組で識別する。同じscope内の異なるInterfaceへ同じlocal nameを割り当てない。 +- 同じlocal nameでもscopeが異なれば別の参照である。Descriptorの内容が似ていても同一性を推測しない。 +- 同じscopeに複数のProviderを登録する場合、そのscope内の名前の衝突はHostが検出・解消する。Scopeを付けるだけで同一scope内の重複が自動的に解消されるわけではない。 + +Scopeはbackendのpath、OSのfilesystem root、URL origin、HTTP endpointのprefixではない。例えばHTTP endpointが`https://example.test/wip`でも、Worldspaceのscopeは`/github`であり、`/wip/github`へ自動的に変わるわけではない。 + +Scope pathの比較にはProtocolのpath規則を用いる。Unicode normalization、case変換、URL decode、文字列prefixだけによる祖先判定は行わない。`/github`は`/github-extra`の祖先ではない。 + +`::`による連結は表記案である。現行のpath segmentには`:`を含められるため、区切りの一意性とlocal nameの許可文字は構文確定時に決める。既存のpath規則へ暗黙に禁止文字を追加しない。 + +## Objectとscopeの関係 + +Objectをpath `P`で公開するとき、`Object.interfaces`の各参照のscope `S`は、`P`へ至る祖先chain上にある必要がある。「祖先」は直近のparentだけでなく、rootからparentまでの各pathを含む。兄弟や別subtreeのscopeは使用できない。 + +`/github/my-rep/issues`の場合は次のようになる。 + +| Interface参照 | このObjectでの利用 | 理由 | +| --- | --- | --- | +| `/::common.read` | 可 | `/`は祖先 | +| `/github::issue.read` | 可 | `/github`は祖先 | +| `/github/my-rep::issue.admin` | 可 | `/github/my-rep`はparent | +| `/gitlab::issue.read` | 不可 | 別subtree | +| `/github/other-rep::issue.read` | 不可 | 祖先ではない | +| `/github/my-rep/issues/123::issue.read` | 不可 | 子孫であって祖先ではない | + +Object自身のpathをscopeとして認めるかは、root ObjectのInterfaceも含めて未確定とする。この草案の通常例はstrict ancestorを使用し、self-scopeを暗黙に導入しない。 + +この制約はInterfaceの自動継承ではない。祖先scopeで宣言されたInterfaceが、配下のすべてのObjectへ自動的に追加されるわけではない。Objectが明示的に公開した`interfaces`のmembershipと、scopeの祖先制約の両方を満たす必要がある。 + +Scopeが祖先であることもauthorizationの証明ではない。現在の主体への公開可視性、Operationの実行権限、validatorによるpreconditionは従来どおり検証する。 + +## Clientによる解決 + +ClientはObjectを観測したpathと、そこに公開された完全なInterface参照を使用する。 + +1. `observe`のrequest pathと各Objectの`name`から配置pathを得る。 +2. `Object.interfaces`の各参照についてscopeがその配置pathの祖先であることを検証する。 +3. 条件を満たす参照を、その完全な表記で`fetch_interface`へ渡す。 +4. Operation callでは同じ参照を`InterfaceTarget.reference`へ設定し、Operation名は従来どおり別fieldへ設定する。 + +Hostは祖先制約に違反する参照をObjectへ公開してはならない。ClientはそのようなObject responseをProtocol違反として扱い、その参照を対象ObjectのInterfaceとして解決・使用しない。既存の分類に合わせ、Client-localな`InvalidResponse`として拒否する案とする。 + +Local nameだけによる検索、近い祖先から順に同名Interfaceを探すlookup、shadowing、scopeを省略した参照の補完は導入しない。`/::issue.read`と`/github::issue.read`の両方が公開されている場合も、完全な参照で明示的に区別する。 + +`fetch_interface`は完全な参照を受け取るため、祖先Objectを順に取得する必要はない。現行requestには対象Objectのpathがないので、「どのObjectから利用するか」の祖先検証はObject観測時とOperation call時に行う。Interface単独の取得可否はHostの公開可視性によって決まり、scope外のObjectから使えないこととDescriptor自体を取得できないことは同一視しない。 + +Hostも`call_operation`で、対象Objectに対するmembershipとscopeの適合を検証する。Clientの検証だけを信頼しない。Scope不適合を含むrequestのerror mappingと検証順は、既存の`InvalidRequest` / `InterfaceMismatch`等との整合を含めて確定する。 + +Operation名とNamed typeのnamespaceは引き続きInterface Descriptor内に閉じる。本案はexternal type importやInterface間の型参照を追加しない。 + +## HostによるproxyとWorldspace合成 + +Clientが扱う絶対pathは、常に接続先HostのClient-facing Worldspaceのrootに基づく。Upstreamのrootや内部mount構成をClientに追跡・補完させない。 + +Upstream Worldspaceのrootを`/github`へ配置する例を示す。 + +| 項目 | Upstreamでの値 | Clientへ公開する値 | +| --- | --- | --- | +| Root | `/` | `/github` | +| Objectの配置path | `/my-rep/issues` | `/github/my-rep/issues` | +| Interface参照 | `/::issue.read` | `/github::issue.read` | +| Repository scopeの参照 | `/my-rep::issue.admin` | `/github/my-rep::issue.admin` | +| `entry`型のpath | `/my-rep/issues/123` | `/github/my-rep/issues/123` | + +別のUpstreamを`/gitlab`へ配置すれば、その`/::issue.read`は`/gitlab::issue.read`となり、GitHub側と衝突しない。 + +さらにこの合成Worldspace全体を`/services`へ配置するHostは、次のように再変換する。 + +```text +/github/my-rep/issues -> /services/github/my-rep/issues +/github::issue.read -> /services/github::issue.read +/gitlab::issue.read -> /services/gitlab::issue.read +``` + +各Hostは自分がClientへ公開する座標系について責任を持つ。単にHTTP requestを中継してWorldspaceの配置を変えないproxyは、scopeを変更する必要がない。 + +### 変換する箇所 + +合成Hostは、表示だけでなくProtocol上の対応を一貫させる。 + +- `observe`のrequest pathをUpstream側へ変換し、返されるObjectの配置・`name`・childrenが公開先のpath規則を満たすようにする。特にUpstream rootの空の`name`を非rootのmount先へそのまま公開しない。 +- `Object.interfaces`のscopeを公開先へ変換する。 +- Clientの`fetch_interface`参照を適切なUpstreamとその参照へ対応付け、responseの`interface`はClientが要求した公開先の参照にする。 +- `call_operation`のtarget pathとInterface参照をUpstream側へ対応付ける。転送先はlocal name単独ではなく完全な参照とtargetの対応で決める。 +- Descriptorで`entry`と宣言されたargumentsをUpstream側へ、resultを公開先へ変換する。Record、list、union、Named typeを辿った内部の`entry`も対象にする。 + +通常の`string`、opaqueな`json`、documentation内のpathらしい文字列を推測して書き換えない。Application固有のdataに別の座標系がある場合は、そのApplication adapterの責務として扱う。 + +型として正しいUpstream pathであっても、公開先へ対応付けられないpathをそのまま返してはならない。公開subtreeの外への`entry`やscopeへの参照をどう扱うかは、明示的な対応付けまたは公開・実行の拒否を含め、Hostの合成方針として定める。Scopeを単に削除したりrootへ付け替えたりして制約を回避しない。 + +### 名前・同一性・validator + +公開先の参照は、移動後のscopeとlocal nameの組に対して衝突なく登録する。配置変更後に同じlocal nameを維持しても、scopeが変わればProtocol上は別の参照である。古い参照と新しい参照の同一性をClientへ推測させない。 + +Scopeの変換だけでObjectの`ref`やvalidatorの安全性が保証されるわけではない。合成Hostは既存の同一性・観測整合性の規則も満たす必要がある。 + +- 異なるUpstreamのObjectへ同じ有効な公開`ref`を割り当てない。 +- 同じ公開Interface参照とvalidatorの組を、異なるDescriptorへ対応付けない。 +- Routingやmountの変更後に、古いObject validatorで別の対象へOperationを実行できないようにする。 +- Upstream validatorをそのまま利用するかHost側で対応付け・wrapするかは、これらの保証を満たす範囲でHostが決める。 + +同じUpstream Objectを複数のscope配下へ配置すると、公開するInterface参照が配置ごとに変わり得る。現行Protocolは同じ有効な`ref`とvalidatorのObject representationが一致することを要求するため、異なるInterface集合を持つprojectionへ同じ公開identityを無条件に引き継げない。この場合の共通祖先scopeでの共有、projectionごとのidentity割当等は、採用前に整理する。 + +## Cacheと表示 + +Interface cacheはClient-facing Worldspaceの接続先・主体等のcontextで分離した上で、完全なscope付き参照とoptionalなvalidatorに基づいて扱う。異なるWorldspaceに同じ`/github::issue.read`があっても、同一Interfaceとはみなさない。ScopeはグローバルなURL namespaceではない。 + +[WIP Text View](text-view.md)では公開先の参照をそのまま引用表示する。表示側でscopeを省略したり、Upstreamの参照へ戻したりしない。 + +```text +object at "/github/my-rep/issues" { + name: "issues"; + description: "RepositoryのIssue collection。"; + interfaces: ["/github::issue.read"]; +} + +interface "/github::issue.read" { + operation list() -> { items: [entry] }; +} +``` + +Text Viewがscopeを定義するのではなく、採用後のProtocol参照を表示する。現在のText View草案のopaque参照例は、Protocol側の採用時に更新する。 + +## 採用前に詰める点 + +- **Self-scopeとroot Object**:Object自身のpathをscopeとして許すか。Strict ancestorだけではroot Objectに利用可能なscopeがないため、rootの扱いを含めて決める。 +- **参照構文**:path内の`::`と区切りの扱い、local nameの許可文字、canonicalな表記、既存opaque参照からの移行。現行のstring fieldを使う案を基本とし、具体的encodingは未確定とする。 +- **Scopeの公開単位**:scope登録とそのpathのObjectの公開・削除の関係。祖先Object自身が同じInterfaceを公開することは要求しないが、registryのlifecycleを定める必要がある。 +- **有効期間と移動**:scopeのrename、mountの付け替え、同じpathへの再登録、進行中callおよび古いcacheの扱い。 +- **複数配置と部分合成**:同じObjectを複数scopeへ投影する場合の`ref`、切り出したsubtreeの外にあるscope・`entry`への対応付け。 +- **Failure semantics**:不正な参照構文、scope不適合、未公開scope / Interface、変換不能な`entry`に対するerrorと、既存の検証順・非公開情報の秘匿との整合。 + +本案はInterface参照のscopeとHostの公開座標変換を定義するためのものであり、Host内部のmount API、Provider登録API、合成設定の共通data structureを同時に規定しない。 diff --git a/draft/unresolved-issues.md b/draft/unresolved-issues.md index 4594af7..095558c 100644 --- a/draft/unresolved-issues.md +++ b/draft/unresolved-issues.md @@ -20,7 +20,7 @@ ### Field semantics -- `Object.interfaces`の要素、`fetch_interface`の`interface`、`InterfaceTarget.reference`で使用するopaque参照のscopeと有効期間。 +- `Object.interfaces`、`fetch_interface`、`InterfaceTarget.reference`の参照scopeと有効期間。[Scoped Interface References(草案)](interface-scope.md)で、絶対pathによるscope、Objectの祖先制約、proxy / Worldspace合成時のHostによる座標変換を検討する。 ### Type / Value