graphos-factory について
graphos-factoryは、GraphOS supergraph向けにREST APIからApollo Connectorsのサブグラフを構築・保守するエンジニア向けスキルです。ワークスペースと決定記録を使い、差分編集でスキーマ変更を保ちます。対応するソースはRESTのみです。
主な機能
- 01REST APIからサブグラフを構築
- 02API一覧と操作選択の管理
- 03決定事項と確認済みの知見を記録
- 04変更差分に沿ったスキーマ編集
- 05検証とカバレッジチェック
- 06リレーションフィールドとテスト雛形の生成
活用シーン
- 01REST APIをGraphOS supergraphに接続
- 02手動編集を保ちながらサブグラフを更新
- 03操作選択、マッピング、依存関係を確認
- 04エクスポート前にスキーマの網羅性と検証を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ apollographql/ skills --skill graphos-factoryインストール後、Claude Code で/graphos-factoryを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
graphos-factory-core/release.envgraphos-factory-core/references/api-discovery.mdconnectors-language.mdcustomer-context.mdlessons.mdmapping-language.mdnaming.mdschema-authoring.mdselection-review.mdspec-intake.mdtesting.mdworkspace-contract.mdgraphos-factory-core/scripts/bootstrap.shcache.shcompose.she2e.shelv2.shlive-env.shlive.shresolve-bin.shtoolchain.shunit.shreferences/export-graphos.mdfederation-subgraph.mdfirst-subgraph.mdverification.mdscripts/bootstrap.shenv.shsession-start.shsupergraph-check.sh詳細
graphos-factory
REST API を、ユーザーの GraphOS スーパーグラフ用の Apollo Connectors サブグラフに変換し、保守しやすい状態に保ちます。成果物はサブグラフです。ユーザーがほかのサブグラフと合成するスキーマです。
生成するのはあなたです。ツールが代わりにスキーマを書き出すことはありません。graphos-factory バイナリ(共通リファレンスでは graphos-factory-core <command> と表記されていますが、ここでは同じバイナリとして横にインストールされた graphos-factory を使います)と、graphos-factory-core/scripts/ 配下のシェルラッパーが機械的な処理を担います。ソースを読み取ってインベントリを作成し、プレースホルダーをレンダリングし、rover を実行し、不変条件を確認します。設計上の判断は一切行いません。
ソースの種類。 workspace.yaml の source_kind がソースを指定します。構築済みなのは rest のみです。OpenAPI ドキュメントの有無を問わず、Apollo Connectors でラップした REST API です。gRPC / protobuf、データベース、既存の GraphQL API は、構築予定ではありますが、まだ構築されていません。ユーザーがそれらのいずれかを求めた場合は、その旨を明確に伝えて処理を中止してください。REST の手順で代用してはいけません。ワークスペースの契約はソースの種類に依存せず、各種類が独自の受け入れリファレンスとキー構文を追加します。
永続化するものはすべて、サブグラフごとに作成する git リポジトリ、つまりワークスペースに置きます。inventory.json はソースが提供する内容、selection.yaml はユーザーが選択した内容、<name>.graphql はエンジニアが編集するサブグラフスキーマ、決定ログ(選択内容とその代替案。decisions.json と、decisions/ 配下の決定ごとのファイルで構成され、graphos-factory decisions 経由でのみ書き込みます)、所見ログ(確定した事実。同様に graphos-factory findings 経由で書き込みます)、そして memory.md には、あなたと前任者が得た知見を記録します。全体の構成については、workspace-contract.md を一度読んでください。コアのリファレンスは graphos-factory-core/references/ に、このターゲットのリファレンスは references/ にあります。
最初のコミット後、スキーマ全体を再生成してはいけません。 差分を算出し、変更箇所だけを編集してください。再生成によって手作業の編集内容が失われることこそ、このスキルが防ぐべき問題です。また、文書化されていない手作業の編集(下記の「手作業の編集」を参照)が残っていると、文書化するまでは apply がブロックされます。
このターゲットの契約は、ユーザーのスーパーグラフにおけるサブグラフの位置づけです。リファレンスで未確定の事項は、各「Open」の見出しに記載されています。現在の仕様としてバイナリが適用する内容は次のとおりです。プレースホルダーは {{BASE_URL}} と {{AUTH_EXPR}} のみで、スキームの接頭辞はプレースホルダーの外側に置きます。また、template.yaml ではそれぞれにローカルテスト値を指定します(export は本番値でレンダリングします。詳細は export-graphos.md を参照してください)。スキーマでインポートできる Federation ディレクティブは @key、@shareable、@requires、@provides、@external、@inaccessible、@listSize、@cost、@tag です(federation-subgraph.md を参照してください)。レイヤーのモデルで想定する @source は 1 つです。2 つ目はリント警告になりますが、禁止ではありません(モデル化されていない状態です)。別のサブグラフが所有する型は、ユーザーの解決済みの決定で宣言された場合に限り、その所有者が付けた名前をそのまま使い、プレフィックスなしで一度だけ定義します(decisions add --foreign-type NAME、federation-subgraph.md §「別のサブグラフが所有するエンティティ」を参照してください)。@tag は GraphOS Contracts 用で、デフォルトでは無効です。ユーザーのスーパーグラフが contracts を使用している場合に限り、contracts ですでにフィルタリングに使われている名前を適用し、決定として記録してください。タグを新たに作ってはいけません。このターゲットではタグ名を指定しないため、unknown-tag が発生することはありません。
最初のコマンドを実行する前のセットアップ。 以下の <skill> は、このファイルがあるディレクトリです。インストール方法(npx skills add、gh skill install、Claude Code プラグイン、ディレクトリのコピー)やエージェント(Claude Code、Codex、Cursor、GitHub Copilot、Gemini CLI など)にかかわらず、bash <skill>/scripts/bootstrap.sh --check を実行し、終了コードが 127 の場合は bash <skill>/scripts/bootstrap.sh を一度実行します(~/.cache/graphos-factory-core/ に約 3 MB を配置します)。すでに実行済みのセッション開始フック(プラグインとして使用するクローン内の scripts/session-start.sh)があれば、このチェックは通ります。その後、シェルで . <skill>/scripts/env.sh を実行します。コマンド間でシェルの状態が保持されない場合は、各コマンドの先頭で実行してください。これにより、graphos-factory、その graphos-factory-core リンク、rover が PATH に追加され、GRAPHOS_FACTORY_CORE_SCRIPTS(以下の $S)が設定されます。ホストにかかわらず、ユーザーの同意があった場合に限り、ツールチェーン(toolchain.sh)をインストールします。新しいワークスペースで最初のサブグラフを作成する場合: first-subgraph.md に従い、init から export までの手順を順番に進めます。それぞれのコマンド、チェック、および想定される非ゼロの終了コードを確認してください。
最初に行うこと
- ワークスペースがある場合は、決定ログ(
graphos-factory-core decisions list .。ユーザーの回答待ちの質問については--openも指定)、調査結果(graphos-factory-core findings list .)、および.factory/memory.mdをすべて読みます。ユーザーからの依頼がない限り、resolvedの決定を再度開かないでください。同じ質問への回答を変更する場合は、graphos-factory-core decisions reopen . --id D-idを実行すると、記録がクリアされてopenに戻ります。その後、decisions resolveで再度確定してください。質問自体が変わった場合は、新しい決定を記録します。記録を直接編集してはいけません。ファイルへの書き込みは、このコマンドを通じてのみ行います。ワークスペースに旧形式の.factory/decisions.mdがあり、decisions.jsonがない場合は、graphos-factory-core decisions migrate .を一度実行してインポートします(D-nnnnの ID は維持され、.mdファイルは削除されます)。その後、作業を続けてください。旧形式のdecisions.json(Hand edit codified:またはSource patch:というタイトルの出所記録や、選択肢のない説明文を含むもの)は、graphos-factory-core decisions migrate . --splitを一度実行して分割します。分割後は、「Write it down」のテストに従って、一覧にある各記録を並べ替えてから--sortedを指定して実行してください(workspace-contract.md §decisions.json)。 - 対象のコントラクト(上記および References に記載)と
naming.mdを読みます。これらに違反すると、テストではなくルーターが実行できるスキーマが壊れます。対象の@sourceとプレースホルダーのルール、および複数語の名前にスネークケースを使う{Service}_Type/{service}_fieldを守ってください。 - 新規の取り込み時、または
select/applyの前には、まず簡単な確認を行います。ユーザーは、仕様に定義されていない顧客固有/テナント固有のフィールド(分析フィールド、CRM カスタムオブジェクト、CMS コンテンツタイプなど)を必要としていますか?必要でなければ汎用ラッパーです。workspace.yamlにcontext_mode: genericを記録して次に進みます(モードが記録されていない場合も汎用として扱われるため、ここで止まる必要はありません)。modelId引数や JSON フィールドだけでは、特化型にはなりません。また、context.yamlも不要です。エンドポイントが識別子や JSON を受け付けるというだけで、テナントモデル、メタデータのエクスポート、認証情報を要求しないでください。特化型である場合、または判断に迷う場合に限り、customer-context.md(References に記載)を読み、そこで評価します。作成に着手する前に、context_modeと依存関係を記録してください。 - ユーザーが必要としている動詞(対象の動詞。
initも含め、以下の表に従います)を選び、指定されたリファレンスを読みます。
動詞
| 動詞 | 最初に読むもの | 作業内容 |
|---|---|---|
discover | spec-intake または api-discovery | inventory.json を作成/更新します。認可された範囲内で顧客のコンテキストを評価し、その記録/スナップショットを更新します。差分を報告します。スキーマや選択内容は決して編集しません |
select | naming, schema-authoring | 対象の操作、ルート、名前、フィールドの除外、タグ、ページネーションについて合意します。すべての [read?] POST を提示し、root: query を指定する前に、どれが読み取りかをユーザーに確認します。selection.yaml を書き込みます(含める各操作に include: true と、その graphql ルートおよび名前を指定します。このファイルを書き込むものはほかにありません)。次に graphos-factory-core selection draft が、含める各操作の response.envelope を提案します。各案をユーザーに確認し、confirmed: true に設定してください。誰も合意していない案をスキーマの形にしてはなりません。また、各 candidate_entity_link ファクトにつき links: エントリーを 1 つずつ作成します(graphos-factory-core inventory links . で一覧表示できます)。各リンクもユーザーに確認してください(confirmed: true、または include: false と reason を設定)。リレーションシップフィールドは親ごとに 1 リクエストとなり、循環参照になるかどうかを判断できるのは作成したスキーマだけだからです。設計上の各質問は決定事項として記録します — graphos-factory-core decisions add . --title T --question Q --choice LABEL … [--multiple](選択肢には 1、2、3…の番号が付きます。すべての --choice が id:label の場合に限り、ID が保持されます)。各決定は、チャット上のやり取りだけで済ませず、graphos-factory-core decisions resolve . --id D-id --chosen N --note … でユーザーと確定します。参照資料から判断できた内容は、インストゥルメントで必要な場合に限り、ファインディングとして記録します(graphos-factory-core findings add . … --cites REF)。必要がなければ記録しません。確定済みの回答を修正するには、graphos-factory-core decisions reopen . --id D-id で再度開き、改めて確定します。 |
apply | schema-authoring, mapping-language, testing | graphos-factory-core lock --check を実行します(コード化されていない手編集があれば停止します。表示された来歴のずれは停止理由ではありません)。graphos-factory-core context check を実行し、作成前にビルド上の不足を解消します。graphos-factory-core reconcile --baseline HEAD で差分を特定します(初回の apply ではスキーマがまだ HEAD に存在せず、すべての範囲が added と表示されます)。該当する範囲だけをスキーマに編集し、すべてのオーバーライドのアサーションを維持します。確認済みの各リンクについて、graphos-factory-core links apply . --dry-run を実行すると、ホスト型に貼り付ける $this をキーとしたフィールドレベルの @connect が出力されます(@key は付けません。呼び出しごとの認証情報を by-id ルートフィールドが持ち、それを引き継ぐ場合を除き、ヘッダーも引数も付けません。その場合は同じ引数と、同じヘッダーまたはクエリパラメーターを使います。nullable な fk では、isSuccess と選択の null ガードを付けます。そうしないとルーターは null の親に対しても GET …/ を送信するためです)。circular、self、no-fk-field、field-exists、nullable-fk、no-host、no-root-field、target-refused(現行のエントリーで、維持するか削除するかの決定が keep と確定していないもの)は拒否されます(connectors-language § Relationship fields)。ほかのエントリーが出力されていても、いずれかのエントリーが拒否されると終了コード 1 になります。graphos-factory-core scaffold で、テストケース、スタブ、および未作成の各操作のユニットテストエントリーの案を作成します(ユニットテストエントリーに含められなかった内容はメモに記載されます)。すべてのプレースホルダーを監査し、次に e2e.sh --generate を実行します。再度実行します(クリーンであること、changed since HEAD ⊆ 差分であること)。変更したすべての操作に対して graphos-factory-core source-coverage . OP-KEY --check を実行し、lock の前に graphos-factory-core source-coverage . --check も実行します(OP-KEY を指定しない場合は、選択したすべての操作が対象となり、失敗した操作がそれぞれ示されます)。両方向で unaccounted と unresolved がゼロであり、unaccounted の動作行がないことが基準です(任意引数には、ソースにある省略についての文をドキュメントコメントに記載するか、behaviour の omit に理由を記載します。ファインディングの not-applicable や決定事項の editorial も該当します)。--check は条件を満たさない場合に失敗します。省略する各パスは、黙って省くのではなく記録します。公開しないことを選んだパスは、公開する選択肢を含む決定事項として記録します。graphos-factory-core decisions add . --title T --question Q --choice omit:L --choice expose:L --resolved --chosen omit --note WHY --omit 'op|direction|path|editorial'。式で消費されるパスはファインディングとして記録します。graphos-factory-core findings add . --title T --body TEXT --cites REF --omit 'op|direction|path|consumed'(各値を引用符で囲み、パイプはそのまま入力します。各バックスラッシュはこの表でのエスケープにすぎません。findings add は editorial を拒否します)。分類器独自の文法による unresolved 行(リクエスト側の ->map(…)->first、リクエスト側の ->match、->match->first のようなメソッド連鎖)(または disc->match(…) の抽象型形式ではない ... スプレッド)は記録し、このレイヤーでは検証されていないことを報告します。inventory.json の編集で解消してはなりません(schema-authoring.md § Coverage)。graphos-factory-core lock を実行し、検証して、apply: としてコミットします |
codify | workspace-contract, mapping-language | スキーマへの手編集が検出されました。エンジニアへの確認または差分から理由を把握し、graphos-factory-core codify --key K --reason R を実行します。意図を selection.yaml に移した後なら --expressed を付け、または --assert kind=value… を付けます。差分からは分からない理由には --context TEXT を付けます(codify は決定事項を記録しません)。固定された仕様を編集した場合は、graphos-factory-core codify --source PATH --reason R を実行します。その後、仕様に基づくワークスペースではインベントリを再構築し(graphos-factory-core inventory build PATH。intake: mixed のワークスペースでは、その後に infer --update-inventory を再実行)、修正内容とそのパッチマークが inventory.json に反映されるようにしてから、reconcile を再実行します。decide: としてコミットします。--key はロックと同期している範囲を拒否します。ツールが 推論した 内容の修正は手編集ではありません。以下の最初の方針を参照してください |
validate | testing, customer-context | ライブリクエストの前に --phase live でコンテキストを確認します。ライブのみの不足は、オフラインのレイヤーを妨げることなく not_run として報告します。compose → unit → e2e → write-body proof → conformance → lint → json-accounting → live の順に実行します(write-body proof と json-accounting のレイヤーは合否判定の対象外です。fail は合格ではなく、未解決のファインディングとして表示されます。evidence/latest.json の contract_version は 2 です)。evidence/latest.json を書き込みます。スキップしたレイヤーはスキップとして報告します。テストケースが 0 件のユニットテスト実行は not_run であり、決して合格にはなりません。ケースが 0 件のスイートはすべて失敗します。ただし、ヘッダーの最初の文が、その決定事項(D-0019 または D-k7m2qx)を引用している場合を除きます。また、skip: true のケースは失敗します。選択した各操作は、ライブケースまたは理由付きのライブ exclusions: エントリーに含めます。lint の link-untested は、ユニットテストエントリーで対象になっていない、または e2e ケースで選択されていない各リレーションシップフィールド(<Type>.<field>)について警告し、エビデンスレポートにはそれぞれが記載されます。nullable な fk に null の親を使う e2e ケース(フィールドを選択するケースに対応する、空セグメントの GET に対するマッピング)がない場合、link-null-untested が警告します。tests/live.yaml が存在する場合、フィールドを選択するライブケースも、そのフィールドを示す field: "<Type>.<field>" の exclusions: エントリーもなければ、link-live-unaccounted が警告します。ユニットテストも e2e テストもないフィールド、null の親を使うケースがないフィールド、またはライブテストから除外されたフィールドは検証されていません。その旨を明示します(scaffold はまだリンクフィールドのテスト案を作成しません。ユニットテストエントリーと e2e ケースは手作業で記述してください。connectors-language § Relationship fields)。オラクルが判定できなかった適合性テストの本文は unchecked(形状が文書化されていない)または unmatched(該当する操作がない、または本文が読み取れない。この場合は失敗)となり、合格にはなりません。受け入れた不足は graphos-factory-core codify --waive TARGET --status S --reason R で記録します。報告前に graphos-factory-core lock --check --provenance を実行します。終了コード 3 の場合、プレーンな graphos-factory-core lock --check が変更済みまたは帰属不明の範囲、固定ソースの問題、編集済みインベントリを報告しないときに限り、再ロックします(この場合は再ロックの推奨が表示され、--json では relock_advisable が設定されます)。ずれが来歴だけの場合(ケース、スタブ、memory.md の行)、またはドキュメントの sources.lock.yaml が固定されなくなった場合、意図的に監視対象から外れるためです。それ以外の場合、スキーマ、固定ドキュメント、または inventory.json がロック下で変更されています。停止して「手編集」の手順に従います(範囲または固定ソースをコード化するか、編集されたインベントリを再構築してください)。再ロックすると、編集の唯一の記録が消えてしまいます |
record <op>… | api-discovery, testing | 各リクエストで graphos-factory-core probe を実行します(指示がない限り GET のみ。ディスクに触れる前にサニタイズされます)。オラクルには graphos-factory-core infer --update-inventory を実行し、スタブには graphos-factory-core fixtures を実行します。仕様と実際の動作との差異に関するファインディングを memory.md に記録します |
このターゲットでは、まず init を実行します(ターゲットを記録し、skill.name: graphos-factory を設定します。--target は省略可能です)。 |
| 動詞 | 最初に読むもの | 実行する内容 |
|---|---|---|
init <name> | workspace-contract | サブグラフ名(snake_case または kebab-case、naming.md)と context_mode(上記の手順 3)を決め、説明ドキュメントを自分で取得してから、graphos-factory init <dir> --name N --spec FILE [--url U] --context-mode generic|specialized を実行します。.factory/workspace.yaml(skill.name: graphos-factory)、作業用コピーとその固定されたベンダーコピー、sources.lock.yaml、inventory.json を sources pin + inventory build とまったく同じように書き込み、さらに 4 つのひな形を作成します。ひな形は <directory>.graphql(仕様書へのリンクと 1 つの @source のみで、ルートフィールドはありません。そのため最初の apply までは compose に失敗します)、template.yaml(BASE_URL はドキュメントにある最初の絶対 URL のサーバーです。なければ、コメントで設定を求めるローカルの代替値です。AUTH_EXPR はスキーム名を示した {$env.<SERVICE>_TOKEN} です。ローカルテスト値のみを使い、本番ホストは使いません。本番ホストは export 時に指定されます)、supergraph.yaml(このサブグラフ 1 つ用の rover compose 設定であり、ユーザーの supergraph 用ではありません)、tests/router.yaml(e2e の override_url)です。ひな形内のすべてのコメントに従い、すでに存在するものはそのままにします。ワークスペースがすでにあるディレクトリでは実行を拒否します。--dry-run --json は書き込みを行わず、各ファイルのパス、内容、sha256 を一覧表示します。git は実行しません。単独で使う場合は、そのディレクトリで git init を実行します。すでにリポジトリ内にある場合(サブグラフごとに 1 フォルダーを置くモノレポなど)は、外側のリポジトリで <name>/ を追跡し、リポジトリを入れ子にしないでください。その後、コミットします。ドキュメントを固定する対象がない仕様書なしのワークスペース(intake: discovered)では、workspace-contract の説明に従って、workspace.yaml(skill.name: graphos-factory を含む)と sources.lock.yaml を手動で作成します |
export | export-graphos | validate の後に限り、graphos-factory export . --out DIR --base-url URL を実行します(DIR はワークスペースの外、URL はユーザーが指定する本番ホスト)。DIR/<directory>.graphql を生成し、rover コマンド、ルーターの認証情報用変数とその最低要件、未検証の項目を出力します。未検証のワークスペース(証拠がない、evidence の実行後にレイヤーが読み取るファイルが変更されている〔該当ファイル名が示されます〕、pass でないレイヤー、実行済みの証拠がない操作、lint エラー)や、絶対 http(s) URL ではない URL、userinfo を含む URL、ローカルの URL は拒否し、その理由を evidence の用語で示します。その問題を修正して(その後 evidence を実行して)再実行してください。決して回避策を使わないでください。実行済みの supergraph_check が失敗している場合も拒否し、rover のエラーを示します。出力をユーザーに渡してください。rover subgraph check や publish を手動で実行してはいけません。GraphOS キーを扱ったり、要求したり、表示したりしてはいけません。また、supergraph_check が pass でない場合や、未検証として列挙された項目をすべて報告してください |
このターゲットでの validate は、証拠レイヤーの supergraph_check も実行します。これは、ユーザーの公開済み variant に対して rover subgraph check を実行しますが、ユーザーの環境に APOLLO_KEY がある場合に限ります(キーはユーザー自身のものです。決して要求、設定、表示してはいけません)。また graph ref が必要です。セッションごとに 1 回、どの <graph>@<variant> か尋ねてから、実行するコマンドに GRAPHOS_FACTORY_GRAPH_REF を設定します。ユーザー自身の GRAPHOS_FACTORY_SUPERGRAPH_CHECK=auto は $APOLLO_GRAPH_REF を使用します(ユーザーに代わって設定してはいけません)。それ以外の場合は not_run です(references/verification.md)。compose レイヤーではサブグラフ単体を compose します。not_run または skipped のチェックは実行されていないものとして報告し、決して合格として報告してはいけません。また、サブグラフがユーザーの supergraph に対してチェックされていないことを明確に伝えてください。
ツール
製品のバイナリが1つと、検証レイヤー用の rover、Java、curl を呼び出す graphos-factory-core/scripts/ 内のシェルラッパーです。バイナリは、スキル独自の scripts/bootstrap.sh によってスキルの GitHub リリースからダウンロードされます(バージョン付きリリースが存在するまでは、継続的に更新される edge プレリリースが対象です)。scripts/bootstrap.sh はスキル自身のセッション開始フックから実行される場合があります。実行されない場合は、自分で実行してください。また、バイナリに graphos-factory-core というリンクを作成します。スキル自体を開発するときに限り、crate/ からビルドします(bootstrap.sh --build)。すべてのラッパーは、まず $GRAPHOS_FACTORY_CORE_BIN からバイナリを取得し、次に bootstrap キャッシュ内のそのリンクから、最後に PATH から取得します。どのバイナリを使うかとそのバージョンを stderr に出力します。ピン留めされたバージョン(crate/Cargo.toml、インストール済みスキルの release.env、または $GRAPHOS_FACTORY_CORE_VERSION)より古い場合は、終了コード 78 で終了します(スキップではなく、失敗です)。evidence はラッパーに自身のバイナリを渡します。78 が返された場合は、bootstrap.sh を再実行するか(チェックアウト内では --build)、GRAPHOS_FACTORY_CORE_BIN を設定してください。これを回避するためにピン留めされたバージョンを下げてはいけません。すべてワークスペースのルートから実行してください。graphos-factory-core <command> [verb] --help(または -h)を実行すると、そのコマンドの使い方が表示され、読み書きは一切行われません。verb で宣言されていないフラグ(タイプミスや、1つの引数に引用符でまとめられた2つのフラグなど)は終了コード 2 で拒否され、処理は何も実行されません。スペルを修正し、フラグを削除してはいけません。compose、unit、e2e、live には rover が必要です。e2e の WireMock には Java が必要です。ワークスペースには何もインストールされません。rover の composition プラグインは Elastic License v2 の対象であり、ユーザーだけが APOLLO_ELV2_LICENSE=accept を設定して同意できます。同意されるまでは、これら4つのレイヤーは not_run になります。一度だけ確認してください。リンクを提示し、明示的に同意された場合に限り、このセッションで実行するコマンドに APOLLO_ELV2_LICENSE=accept を設定し、同意を memory.md に記録してください。尋ねずに設定してはいけません。また、シェルのプロファイルに export を記述すれば設定が永続化されることを伝えてください。
S=path/to/graphos-factory-core/scripts
export GRAPHOS_FACTORY_CORE_SCRIPTS=$S # evidence needs to find the wrappers
# If the host supplies a model identifier, set GRAPHOS_FACTORY_CORE_LLM_MODEL to that exact value.
# Never infer or guess a model identifier. The lock records unknown when this value is absent,
# and `lock` prints a one-line warning for that and for a skill source of binary-build.
graphos-factory-core inventory build openapi.json # description document (OpenAPI 3.x or Swagger 2.0) -> .factory/inventory.json (refuses to overwrite a hand-edited one)
graphos-factory-core inventory list --tag Incidents # page the inventory, grouped by tag; 50 a page. --json: total, offset, limit, next_offset (null on the last page) — a loop over every operation follows next_offset
graphos-factory-core inventory list --support needs_review # what still needs a decision
graphos-factory-core inventory describe get:/v2/incidents # one operation, shapes expanded
graphos-factory-core inventory links . [--json] # every candidate_entity_link fact flat: shape, property path (album_id / []>album_id / songs[]>album_id), the canonical GET-by-id operation, whether the host is a list item and whether that operation is selected, then every GET-by-id refused as a target because its response does not carry its key — a hint, never a finding; judge each and record the call with `graphos-factory-core decisions add`
graphos-factory-core inventory diff old.json new.json # what a spec refresh changed
graphos-factory-core context check . [--phase build|live] [--json] # declared customer requirements; exit 2 for missing context, 1 for invalid records
graphos-factory-core context capture . (--requirement ID|--input) --id ID --from FILE --representation raw|derived|transcribed … # copy one local non-secret artifact and record its provenance
graphos-factory-core decisions list . [--open] [--causal] [--json] # the decision log: decisions only — calls with their alternatives, resolved, and open questions awaiting the user. The records of `.factory/decisions.json` (numbered `D-nnnn`, never added to), then one file per record added since under `.factory/decisions/` (random `D-k7m2qx`); --causal orders by after/amends and shared affects, a `via:` line per edge
graphos-factory-core decisions add . --title T [--question Q] [--choice LABEL]… [--multiple] [--phase P] [--affects PATH]… [--resolved --chosen N --note TEXT --decision TEXT --by user|agent] [--omit 'operation|direction|path|reason']… (a resolved request omit also removes an optional source body member from the write-body proof; a member the source requires stays a gap, `required_member_omitted`, and `decisions add` warns when an omit targets one) [--json-reason 'Type.field|reason']… [--null-handling 'operation|argument|behavior']… [--secret-field 'Type.field|disposition|reason']… [--foreign-type NAME]… [--slug S] [--after D-id]… [--amends D-id]… # record a decision, as its own file with a random id, printing `N label` per choice (choices numbered 1, 2, 3… in flag order; ids are kept only when every `--choice` is `id:label`, else each value is a whole label; a duplicate id, or an invalid one when every value looks like `id:label`, is refused at the flag) (--after names decisions it presumes, --amends ones whose answer it changes where their affects overlap): an open question, or with --resolved a call already made (`resolution.by` defaults to agent; --note says why you did not ask); refuses, exit 1 `no-alternative`, a record that names its alternative none of three ways — a --question, two or more --choice, or an --omit with reason `editorial` — a settled fact is `findings add`; --omit records a structured scope exclusion `source-coverage` reads, only on a resolved record, and an `editorial` omit lives only here; path `.` omits the whole direction for that operation, which is how to record an EmptyResponse 204's empty root row; --json-reason records why a response field is left typed as the workspace's JSON scalar, from `crate::json_accounting::REASONS`, read only on a resolved record — never a selection.yaml field or a schema doc comment; --null-handling records what the connector sends for an explicit null argument (behavior: send_null or omit), read only on a resolved record; --secret-field records a reviewed credential-shaped response field (disposition expose or exclude) that lint's secret-field-exposed rule reads, only on a resolved record; --foreign-type declares a type another subgraph owns, which the schema may then declare under its owner's exact name, read only on a resolved record and only by a target that allows it (that target's references say where); direction `behaviour` waives an omission sentence an argument's doc comment leaves out, path `query:NAME`/`path:NAME`/`header:NAME`/`body:KEY.PATH`, reason `editorial` or `not-applicable`
graphos-factory-core decisions resolve . --id D-id [--chosen N|LABEL]… [--note TEXT] [--decision TEXT] [--by user|agent] [--force] # settle an open decision (with --force it replaces the whole resolution: pass the old --decision and the old note plus what you add, to amend a record without losing it); the same command a headless agent and a host UI both use
graphos-factory-core decisions reopen . --id D-id [--json] # revise a decision: clear a resolved/superseded record's resolution and set it back to open (everything else kept); refuses an unknown id, an open record, or a missing/invalid log
graphos-factory-core decisions supersede . --id D-id [--json] # mark a resolved decision replaced: status only, its answer kept; it stops counting as resolved everywhere (its omits, json_reasons and null_handling, its affects, a links: decision: naming it), so re-record what still applies; the fix for lint's `stale-omit` (a finding's: `findings supersede`)
graphos-factory-core decisions link . --id D-id (--after D-id | --amends D-id)… [--json] # edges on a decision recorded as its own file, the fix for lint's `decision-overlap`; an old D-nnnn record never gains a field, so put the edge on the newer record
graphos-factory-core decisions migrate . [--keep-md] [--force] [--dry-run] [--json] # one-time: import a legacy .factory/decisions.md into decisions.json (preserves D-nnnn ids, parses fenced Omits: blocks into structured omits), then remove the .md
graphos-factory-core decisions migrate . --split [--sorted FILE] [--dry-run] [--json] # one-time, an older-format log (FILE: YAML/JSON mapping each listed D-nnnn to `{as: decision, question, choices, chosen, by, note}` | `{as: finding, cites, body, …}` | `{as: memory, line}` | `{as: drop}`; without it the unassigned records are listed and nothing is written, exit 1 `unsorted`): codify records move onto the override, waiver or patch they shadow, refresh records become findings, a record with a question, choices or an editorial omit stays a decision; lists every other record for you to sort by hand and refuses to write without --sorted until each is assigned
graphos-factory-core findings list . [--json] # the findings log (`.factory/findings.json`, then one file per finding added since under `.factory/findings/`): facts a reference or the wire settled, `F-nnnn` or random `F-9k2wde`, current until superseded; every omits/affects reader takes these with the resolved decisions
graphos-factory-core findings add . --title T --body TEXT [--cites REF] [--source agent|codify|sources] [--affects PATH]… [--omit 'operation|direction|path|reason']… [--evidence E]… [--related D-id|F-id]… # record a fact an instrument needs, or the next session does; omit reason `consumed` or `not-applicable` — refuses `editorial`, which is a decision
graphos-factory-core findings supersede . --id F-id # a later finding replaces it; it stops counting
graphos-factory-core source-coverage . [OP-KEY] [--json] [--check] # source coverage: every fact the source offers is covered by the schema, or a resolved decision or current finding says why not (`spans obligations` is the old spelling); every request-body and response path the source offers (parameters are not classified), classified as mapped / consumed / omitted-decided / unaccounted / unresolved, plus unverified-default and transport-expansion-missing at an `x-expansion` boundary, plus a behaviour section when an optional argument's source says what omitting it does (documented / waived / unaccounted); zero of those four in both directions and zero unaccounted behaviour is the completion bar, and --check fails closed on any; with no OP-KEY it checks every operation selection.yaml includes, one counts line each, --json wraps each operation's object in `operations` with `totals` and `failing`, an operation it cannot classify counts failing, and nothing selected exits 1; a resolved omit no row needs any more (the path is now mapped, envelope-read for an `editorial` entry, or no longer offered) is a `stale-omit`, counted and listed with its decision id but not part of the bar, and lint warns on it
graphos-factory-core spans json-accounting . [--json] [--check] # every response field the current SDL still types as the workspace's JSON scalar, nested and list-wrapped fields included, matched against a `json_reasons` entry on a resolved decisions.json record (never selection.yaml or a schema doc comment — the decisions-only rule) for a reason from a closed vocabulary (free-form-object, recursive, vendor-undocumented, polymorphic-without-discriminator, depth-cap) whose predicate is re-checked against inventory.json every run — accounted / unaccounted / stale (recorded reason no longer holds) / recoverable (inventory is now fully typed there, overrides the rest) / unresolved (reason recorded, but no shape located for the type: by name, root-field operation or parent property); `defaults.fields: all` is never itself a reason, and --check fails closed on anything but accounted
graphos-factory-core serialization . [--json] # read-only: the standalone CLI for the write_body_proof evidence layer; per body-sending write, each argument's value proven at its own body pointer / query key / path or header placeholder by an executed e2e case (needs the run log), omission at its pointer, explicit null per selection.yaml `defaults.null_handling` (state `omit`) or a per-argument null_handling decision; an argument the connector spells in a way the reader cannot place is placed by an executed case whose stub demands its value at one body pointer (`ambiguous_value` when the value is not distinctive; `--json` lists `placements`, static or executed); unproven_mapping and null_handling_undecided are open findings, never passes; replaces list-arg-unproven / mutation-cases / loose-write-body / unit-no-body
graphos-factory-core reconcile . --baseline HEAD # schema <-> selection <-> inventory delta; overrides; hand edits; what moved since HEAD; exits 2 with the URLs to compare when not one connector pairs (a base-URL split)
graphos-factory-core sources pin . --path openapi.json [--url URL] # keep the vendor's bytes as .factory/sources/*.upstream.*; record kind/version/hashes
graphos-factory-core sources status . [--json] # every pinned document: kind, upstream integrity, hand edits, patches; plus every recorded docs/probe source (url, retrieved_at, sha256, operations, note)
graphos-factory-core sources refresh . --path openapi.json --from FILE [--url URL] --reason R # a newer vendor document: re-apply patches[] (re-applied / obsolete / conflict), rebuild + diff the inventory; both shas and each patch's fate go to a finding (source: sources)
graphos-factory-core lock . [--check [--provenance]] [--skill-dir DIR] [--model MODEL] # record applied hashes plus skill, binary, toolchain, input and output provenance; --check also lists provenance drift, --provenance fails on it; a write warns on stderr when the model is unknown or the skill source is binary-build, naming --model / GRAPHOS_FACTORY_CORE_LLM_MODEL and --skill-dir / GRAPHOS_FACTORY_CORE_SCRIPTS
graphos-factory-core codify . --key K --reason R [--context TEXT] --assert contains=… # turn a hand edit into an override (reason, context, assertions), or --expressed (a finding only when --context is given); no decision record — --decision D-id (numbered or random) only attaches a real one
graphos-factory-core codify . --source openapi.json --reason R [--context TEXT] # turn a hand edit to a pinned spec into patches[] carrying reason and context; no decision record
graphos-factory-core probe . --key "get:/x/{id}/" --url URL # one recorded, scrubbed live request -> .factory/samples/
graphos-factory-core infer . --update-inventory # samples -> shapes (the no-spec conformance oracle)
graphos-factory-core fixtures . # recordings -> case-scoped WireMock stubs (.factory/recordings.yaml)
graphos-factory-core scaffold . [--op KEY] [--force] [--dry-run] # draft a case, a stub and a unit entry per selected operation from the connector + shapes (a sparse-fieldsets GET: an omitted and a `_fields_narrowed` case, no unit entry for a comma default); audit before --generate
graphos-factory-core scaffold . --op KEY --status CODE|all-missing # error cases: per documented non-2xx status a case with `# expect-upstream-status: CODE` and a stub answering it (the documented body, if any); refuses a stub equal to an existing one, or to another case planned in the same run
graphos-factory-core error-statuses . # one line per included operation: root field, documented non-2xx statuses, the statuses the last evidence run saw answer its own request, operation key (`-` for none); for e2e.sh, and to see what is still owed
graphos-factory-core selection draft . [--links|--envelopes] [--op KEY] [--force] [--dry-run] # propose a response.envelope per included operation and a links: entry per candidate_entity_link fact; every block lands `confirmed: false` for you to confirm
graphos-factory-core links apply . --dry-run [--link "<shape> > <path>"] [--json] # print the field-level @connect for each confirmed links: entry (the by-id selection verbatim, {$this.<fk>}, the one @source, no @key, the root connector's static headers and queryParams, and the by-id root field's per-call credential mirrored or none; a nullable fk gets the isSuccess and selection null guards); refuses circular / self / no-fk-field / field-exists / nullable-fk / no-host / no-root-field / target-refused (a stale entry lint flags link-target-refused, until its keep-or-drop decision is resolved `keep`); never writes the schema
graphos-factory-core selection set . --op KEY… --include true|false [--dry-run] [--json] # toggle an operation's include flag in place, block or one-line flow entry, comments and anchors preserved (drives a UI toggle)
graphos-factory-core selection review . [--candidate FILE --expect-input TOKEN] [--expect-review TOKEN] # read-only JSON snapshot/review; never saves or applies
graphos-factory-core batch find . [--json] [--all-types] [--check] # per keyed type (graphql.entity), the operations returning an array of it and whether one takes a list of its key: batchable / partial-shape / needs-scope / style-conflict / paginated / list-no-key-filter / none (reads only: GET, an inventory read, or a selection root: query); with graphql.batch: true it prints a paste-ready type-level $batch connector (you paste it; nothing writes the schema); --check exits 1 when a batchable keyed type has no type-level $batch connector and no graphql.batch: false (lint batchable-entity-unbatched); 2 when the schema does not parse
graphos-factory-core scaffold . --op batch:<Shape> # for a type with a $batch connector: a case through a root whose selection maps the key alone, its root stub and an `x-required` lookup stub demanding the exact key list — e2e fails unless the router makes that request
bash $S/compose.sh . # rover supergraph compose (layer 1)
bash $S/unit.sh . [--only PATTERN] # rover connector test (layer 2)
bash $S/e2e.sh . [--generate] [--only PATTERN] # WireMock (java) + real Apollo Router; every stub loads once, for the whole suite — a stub's request matcher must be unique workspace-wide (layer 3); a case's `# expect-upstream-status: CODE` fails it unless the operation's own request was answered CODE (a status served to a nested lookup or side call is not the operation's), an error case without one on an operation documenting several statuses is UNPROVEN, never a pass, and a documented status no passing case saw answer its own request is `not_run` in `error_coverage` and leaves the operation `unchecked`
graphos-factory-core validate . # fixture/request bodies vs the spec (layer 4)
graphos-factory-core lint . # contract + coverage invariants, incl. fixture-collision (two stubs matching the identical request) and fixture-overlap (a stub matching every request a sibling matches: add the `absent` matchers it names, or a priority), sparse-fieldsets (a GET's `fields` default derived and forwarded; `sparse_fieldsets: {enabled: false}` opts out), and undocumented-root-field / argument-constraints-undocumented (a root field or constrained argument with no doc comment, or an optional argument whose doc comment drops the source's omission sentence, "If not passed, …" or "Defaults to …", traced through headers and nested write bodies too; the finding quotes the text to write), and argument-required-optional-in-source (an argument made `!` over an optional source parameter, error); secret-field-exposed (a response field named like a credential — token/secret/password/passcode/credential(s), or an api/private/client/secret/encryption/signing/access `*Key` compound; exclude it or `decisions add --secret-field 'Type.field|expose|reason'`); decision-without-alternative (a decision record with neither a question nor choices, warning); decision-id-duplicate, decision-link-unresolved and decision-link-cycle (a merge kept one id twice, or an after/amends dangles or loops, errors) and decision-overlap (two resolved decisions, at least one recorded as its own file, decide the same span and neither names the other: `decisions link`, warning); entity rules for every @key type: entity-without-lookup (per resolvable key; `resolvable: false` stubs exempt) and entity-key-not-embedded (read through the selection) (errors), entity-without-consumer and entity-field-unresolved (warnings); and two more warnings: unknown-tag (a `@tag` outside the target's vocabulary) and failure-case-missing (per operation, the documented non-2xx statuses no e2e case exercises, meaning the operation's own request was answered with it, not a nested one) (layer 5)
bash $S/live.sh . # tests/live.yaml against the real API (layer 6)
graphos-factory-core evidence . --scripts $S # run every layer, write .factory/evidence/latest.json (includes write_body_proof, in-process, same-run against wiremock_e2e's own current results — never a stale latest.json)
graphos-factory-core render . --out /tmp/x # the placeholder-free copies the wrappers `eval`: stdout is shell assignments (RENDERED_SCHEMA, COMPOSE_CONFIG; ROUTER_CONFIG and its router.yaml only with a --wiremock-port, --router-port or --health-port flag); not a hand-off: the user-facing render is a target's `export`, where it has one
bash skills/<skill>/scripts/bootstrap.sh [--build] # install this skill's binary and its graphos-factory-core link (download; --build from crate/)
cargo test --manifest-path path/to/crate/Cargo.toml # the instruments' own tests
大きな生の仕様を読まずに済むよう、inventory list と describe が用意されています。インベントリをページ単位で読み込み、オペレーションを1つずつ展開してください。--limit が指定されていない場合、1ページには50個のオペレーションが含まれます。operations だけではインベントリ全体を確認できません。next_offset が null になるまで、--offset <next_offset> を指定して呼び出し続けてください。
このターゲットのツール:
graphos-factory init <dir> --name N --spec FILE [--url U] [--retrieved-at T] [--created-at T] [--context-mode generic|specialized|undecided] [--dry-run] [--json] # a new spec-backed workspace: workspace.yaml (skill.name graphos-factory), working + vendor copy, sources.lock.yaml, inventory.json; refuses an existing workspace; never runs git
graphos-factory export . --out DIR [--base-url URL] [--json] # the gate over evidence/latest.json, then DIR/<directory>.graphql rendered with the production host and the rover hand-off (link fields lint leaves not validated among what it did not verify); exit 1 refused, 2 usage; never runs rover
GRAPHOS_FACTORY_GRAPH_REF=<graph>@<variant> bash "$GRAPHOS_FACTORY_TARGET_SCRIPTS/supergraph-check.sh" . # what evidence's supergraph_check runs (APOLLO_KEY from the user's environment): exit 0 pass, 1 fail, 3 not_run, 127 skipped
ターゲットのコマンドは export だけです。上記のその他のツールはすべてコアのものです。検証済みのワークスペースで、エンドツーエンドのパスを1つ確認できます。gitea(Swagger 2.0、ピン留めしてパッチ適用済み)では、CI にキーがないため、supergraph_check は not_run と記録されています。公開前には、シード済みの Gitea に対する書き込みとライブ実行を含め、すべてのレイヤーが上流で実行されます。このリポジトリの CI で再実行されるのは、オフラインチェック(lint、validate、reconcile、lock --check --provenance)のみです。
毎回適用するポリシー(理由は参考資料に記載)
-
Query と Mutation は意味上の区別であり、ユーザーがそう指定するまでは POST は書き込みです。 インベントリではすべての POST を
writeとして記録し、名前から読み取り操作と思われるものにはread_hint(inventory listでは[read?])を付けます。selectの段階では、ヒントが付いた POST をすべて列挙し、いずれかにroot: queryを設定する前に、どれが読み取り操作かをユーザーに尋ねます。selection.yamlには明示的なgraphql.rootが記録されます。Query として選択された POST については、その理由を判断記録として残す必要があります(selectで提起した未解決の質問への回答、またはqueryとmutationの選択肢を指定したgraphos-factory-core decisions add . --resolved …)。Query配下の書き込みは、呼び出し元、キャッシュ、再試行によって安全な操作として扱われるため、その選択はレビューできる状態でなければなりません。 -
インベントリは事実、選択は判断です。
inventory.jsonはソースをそのまま読み取ったものです。すべてのフィールドをドキュメントと照合でき、いつでも再生成できます。また、手動編集は決して行わず、スキーマを左右することもありません。スキーマまたはテストを変更する判断(GraphQL のルート、フィールド名、レスポンスのエンベロープ、公開するページネーション情報、タグ、除外など)はすべてselection.yamlに記録します。ツールはここにヒントとして下書きし、ユーザーと確認します。推論が誤っていた場合、インベントリを修正してはいけません(ロックがハッシュを記録しているため、lintはunacknowledged-inventory-editを報告し、inventory buildは上書きを拒否します)。ピン留めされた作業用コピーのドキュメントを修正してcodify --sourceを実行するか、希望する判断をselection.yamlに記録してreconcileを再実行します。codify --keyはどちらにも使いません。これはスキーマへの手動編集を記録するコマンドであり、同期済みの範囲は拒否します。 -
リレーションシップのリンクは
links:に記録する判断です。 インベントリのcandidate_entity_linkという事実は候補を提示するだけです。selection draftはconfirmed: falseのエントリを書き込み、未確認の下書きは決して適用されません。フィールドは適用されず、reconcile はその旨を記録し、lint は警告します。確認済みのリンクは$thisをキーとするフィールドレベルの@connectであり、@keyは使いません。by-id のルートコネクターの認証情報を引き継ぎます(@source認証には設定しません。connectors-language.md § Relationship fields を参照)。また、nullable な fk では、親が null の場合にGET …/で失敗せず null を返すようにガードします。エンティティ形式はサブグラフ間および$batchの例外であり、判断記録が必要です。現在のルールで拒否されるターゲットを持つ確認済みリンク、または対応する事実がなくなったリンクは古くなっており、そのフィールドについて未解決の判断が発生します。選択肢はkeepとdropで、拒否理由をコンテキストとして記録します。decisions addコマンドを実行すると、reconcile と lint が判断記録を出力し、その名前をエントリのdecision:に表示します。未解決の間、そのリンクは reconcile のドリフトであり、link-target-refusedとlinks applyのtarget-refused拒否が発生します。解決済みのkeepはそのリンクを例外扱いします。解決済みのdropの場合は、判断を引用してinclude: falseを設定し、次回の apply でそのフィールドと手書きのテストだけを削除します。決して再生成してはいけません。 -
実行可能な SDL のデフォルト値は決して設定しません。
pageLimit: Int = 5は、呼び出し元が引数を省略したときにルーターが送信する値であり、利用するエージェントが「使う値」としてコピーします。pageLimit: Intと記述し、プロバイダーのデフォルト値を、そのminimum、maximum、enumとともに(インベントリに記録されている場合)、引数のドキュメントコメントに記載します(schema-authoring.md § Argument constraints)。オフラインのレイヤーはその違いを認識できません。唯一の例外は sparse-fieldsets 引数(fields: String = "…")で、デフォルト値は lint が導出して必須とします(§ Sparse fieldsets)。引数のドキュメントコメントのその他の部分はソースの文を最大 1 つとし、サービスごとのオプトインとします。また、型や必須かどうかを記載してはいけません(§ Argument descriptions)。 -
型なしは到達不能より優先しますが、型ありは型なしより優先します。 形状を正直に型付けできない場合に限り、
<Prefix>_JSONスカラーを出力します(connect/v0.3ではあらゆるoneOf、v0.4 ではワイヤー値からメンバーを識別できないもの、または独自のコネクターで再取得できない場所にネストされたもの、自由形式オブジェクト、additionalPropertiesマップ)。理由はドキュメントコメントに記載します。v0.4 では、識別子のあるoneOfはユニオンまたはインターフェースとし、... kind->match([wire, { __typename: "Member", … }], …, [@, null])としてマッピングします(mapping-language.md § Abstract types)。ペイロードデータを捨てる、根拠のない型推測は決して行いません。 -
エラーのメッセージパスは、推測した慣例ではなく、インベントリに記載された形状から取得します。 式を書く前に、ソースの
errorsブロックまたはコネクターのerrorsブロックが対象とするオペレーションについて、errors[].shape_refを確認します(schema-authoring.md § Errors)。lintのerror-path-unresolvedがこれを検査します。 -
含めるオペレーションはすべて、実行済みの証拠が必要です。ケースとスコープを限定したフィクスチャ、引数がスカラーの場合はユニットエントリー、さらに仕様または推論したスキーマとの適合性チェックが必要です。スキップしたレイヤーは成功ではなく、スキップとして報告します。
-
フィクスチャは真実です。 テストを通すために記録済みのフィクスチャを編集してはいけません。スキーマ、ケース、または期待値を修正します。コミット前に機密情報を除去します。コミットするファイルには認証情報の環境変数名を記載し、値は決して記載しません。
-
.factory/のファイルは通常のファイルであるか、存在しないかのどちらかです。 すべてのツールは、単一の管理モジュールを介して.factory/*を読み書きします。パスのどの構成要素(.factory自体、sources/、ファイル)がシンボリックリンクであっても、読み取り・書き込みの両方で拒否し、メッセージにはリンク先ではなくワークスペース相対パスを示します。refused — a symlink, not a regular fileは、問題がツールではなくワークスペースにあることを意味します。実ファイルを復元して(git checkout --で戻すか、コピーして配置して)再実行してください。自分でリンク先を読み書きして回避してはいけません。 -
すべてを明らかにします。 API にあるすべてのオペレーションは、
selected、理由付きのexcluded、理由付きのunsupported、またはunresolvedのいずれかでなければなりません。「何が未選択のまま残ったか」は、ユーザーが実際に尋ねていることです。 -
記録を残します。判断基準は 1 つの質問です。 妥当なエンジニアが別の選択をする可能性があり、そのサービスもなお有効であり得るか?(形式については workspace-contract.md を参照)
- はい →
graphos-factory-core decisions addを使い、質問とすべての代替案を記録します。ユーザーが解決するか、質問せずに判断できるだけの確信があった理由を--noteで記録して自分で解決します。 - いいえ。参照資料またはワイヤー仕様で決まるため →
graphos-factory-core findings add --cites …を使います。ただし、ツールがomitsまたはaffectsを必要とする場合、あるいは次のセッションで事実が必要な場合に限ります。 - ベンダー固有の挙動、停止したエンドポイント、認証の詳細 →
memory.mdに記録します。また、## Tried and rejectedの下に記録するものは、否定的な結果、ツールでは免除できない許容済みの残存事項、ユーザーから受けた継続的に適用する修正指示です。これらはいずれも現在の状態からは導出できず、次のセッションではgit logではなくmemory.mdを読みます。 - 測定値、以前の記録への修正、退避、または「発見して修正した」内容 → コミットメッセージ、または修正対象の記録を編集して残します。
- 次のサービスで必要になる情報 →
graphos-factory-core/references/lessons.mdに記録します。 仕様の修正は、これらのどれにも該当しません。作業用コピーを編集してから、graphos-factory-core codify --sourceを実行すると、その理由とともにsources.lock.yamlのpatches[]として記録されます。稼働中の証拠はverifiedとして追加してください。
- はい →
-
ユーザーのキーがない場合、サブグラフ単体でチェックします。 Compose、 unit、e2e、conformance、live では、サブグラフ単体を検証します。ユーザーの他の サブグラフと連携できるかどうかは、
supergraph_checkが成功するか、rover subgraph checkを実行するまで未検証です(references/verification.md)。すべてのレポートでその旨を伝え、 エンティティまたは@shareableの選択はすべて decision として記録してください(references/federation-subgraph.md)。
検出してから選択し、その後に適用する
discover はスキーマも選択内容も編集しません。インベントリを再構築し、
追加・削除・変更されたオペレーションを報告し、顧客のコンテキストを評価します。
要件や認可済みのメタデータスナップショットを永続化することはありますが、未解決の入力は
context.yaml に表示されたままです。ユーザーが
selection.yaml を変更します(手作業、ホスト UI、またはあなたへの依頼による変更)。その後に限り、apply は
差分で指定されたオペレーションに対してのみスキーマに変更を加えます。
手動編集:検出し、記録し、編集可能な状態を保つ
.factory/applied.lock.yaml は、最後に書き込んだスキーマを表します。スパンごと(ヘッダー、各型、オペレーションキーごとの各ルートフィールド)に 1 つのハッシュがあり、固定された各ドキュメントと inventory.json にもそれぞれ 1 つずつあります。
ハッシュが異なるものはすべて手動編集(エンジニアまたはホスト UI による編集)であり、graphos-factory-core reconcile、lock --check、lint はいずれもその内容を示します。編集された inventory.json には記録すべき内容がありません。これは構築されるもので、編集されるものではないため、再構築して、該当する場所(固定されたドキュメント、または selection.yaml)に修正を加えてください。また、複数のオペレーションのパスが同じように一致するルートフィールド(unattributed-span)も同様です。オペレーションを selection.yaml で宣言してください。lock と codify はこれを拒否します(schema-authoring.md)。
手動編集が 1 つでも存在する間は、決して apply を開始しないでください。その編集を自分の差分と区別できず、選択内容に合わせて「修正」することこそ、このスキルが防ごうとしている問題です。
見つかった場合は、他の作業に先立って記録してください。
- 理由を把握します。 ファイルの
git logとスパンの diff を確認してください。 理由が明らかでない場合は、エンジニアに尋ねてください。理由を決してでっち上げないでください。 - 選択内容で表現できるなら、そちらを優先します。 編集内容が
selection.yamlで 表現できるもの(タグ、名前変更、除外、ルート、名前など)であれば、そこに記述し、graphos-factory-core reconcileでそのオペレーションにドリフトがないことを確認してから、graphos-factory-core codify --key K --reason R --expressedを実行してください。次の apply では 選択内容から編集が再現され、何も固定されません。 - それ以外の場合は、意図をアサートします。
graphos-factory-core codify --key K --reason R --assert contains=… --assert tag=…は、オーバーライドとして記録します。つまり、スパンのバイト列ではなく、そのスパンについて維持すべき条件を記録します。付随的なテキストではなく、編集の要点(説明コメント、順序、追加の引数など)に対するアサーションを記述してください。--untilで見直す時期を指定し、--expiresで reconcile にその旨を表示させます。--pin(アサーションなし)はスパンを固定するもので、最後の手段です。lint はこれに警告します。 decide:をコミットします。 codify はオーバーライドに理由(reasonと、diff では分からない内容を記述する--context TEXTによるcontext)を書き込み、ロックを更新しました。decision は記録しません。また、--decision D-idは、そのエントリーを実在する decision に関連付けるだけです。理由がローカルな好みではなくベンダーの事実である場合は、memory.mdに 1 行追加してください。
オーバーライドによってオペレーションがロックされることはありません。後から選択内容やインベントリの変更でオーバーライド対象のスパンが指定された場合は、他と同じように編集し、すべてのアサーションを満たしたままにしてください(reconcile がチェックします)。引き継いだ意図を apply: コミットに記述し、graphos-factory-core lock を再実行してください。変更によってアサーションを満たせなくなる場合は、作業を止めて確認してください。どちらかを独断で選ばないでください。
スパンに手を加える前に、オーバーライドの reason と context を読んでください。理由はそこに記されています。
同じルールがピン留めされた仕様にも適用されます。 graphos-factory-core sources pin --path openapi.json はベンダーのバイト列を変更せずに
.factory/sources/openapi.upstream.json として保持し、作業コピーを
applied.lock.yaml で記録します。それ以降、openapi.json はスキーマと
まったく同じように編集できます。仕様に誤りがある場合(API が null として送るフィールド、レスポンスの欠落、型の誤りなど)は、作業コピーを編集し、上流ファイルは決して編集しないでください。また、その変更を明文化します。graphos-factory-core codify --source openapi.json --reason R は、上流ファイルとの差分を構造的に計算し(書式の違いは対象外)、各操作に理由(指定されていれば --context も含む)を付けて、エントリーの patches[] に記録します。lock --check、reconcile、lint は、明文化されていない仕様の編集を (unacknowledged-source-edit) として示し、明文化されるまで apply を拒否します。変更された上流ファイルは復元すべきエラーであり、明文化の対象にはなりません。新たに取得したベンダー文書は、sources refresh --path openapi.json --from FILE --reason R で更新して取り込みます(パッチを再適用し、それぞれについて再適用、不要、競合のいずれかを報告します。検出結果には両方の SHA と各パッチの結果が記録され、インベントリが再構築されて差分が表示されます。spec-intake.md を参照してください)。ベンダー文書をパッチとして明文化したり、作業コピーに上書きしたりしてはいけません。終了コード 3 は、意図を手動で再適用して明文化する必要がある競合を示します。終了コード 4 は書き込みが中断されたことを示します。指定された git checkout で復元してください。すべての discover は作業コピーを読み取るため、インベントリにはパッチが触れる操作が示されます。ベンダーの誤りを証明する verified:(サンプルまたはページ)をパッチに手動で追加してください。これがないパッチは推測にすぎません。
apply の完了
コンテキストの準備状況と未解決の実行時要件は、コネクタのテスト結果とは別に報告してください。そのうえで、次の順に報告します。
- 作業開始前の
graphos-factory-core lock --check(手動編集なし)。 - 追加、変更、削除された操作(編集前の
graphos-factory-core reconcileレポートと、その後のclean行。changed since HEADに記載されるのは、差分がカバーする範囲のみ)。 - すべてのオーバーライド:アサーションが
okであること、選択内容からの乖離(エンジニアによるもので、修正されていないこと)、変更したかどうかとその理由。 evidence/latest.jsonの検証表。スキップされたレイヤー、または未実行のレイヤーとその理由をすべて記載します。- 記録されたスキルのリビジョン、ダーティ状態、コミットハッシュを含む、
graphos-factory-core lockの結果。
出所の記録は、作成された状態を示します。モデルの値は自己申告です。モデルの身元やセッション履歴全体を証明するものではありません。ビット単位での再現性を保証するものでもありません。
説明するのではなく、ツールの合格出力を引用してください。選択された操作に実行済みの証拠がひとつでもない場合、ワークスペースは検証済みではありません。実行されたレイヤーを列挙するのではなく、そのことを明確に伝えてください。
ユーザーからサブグラフがどのようにグラフに反映されるかを尋ねられた場合は、references/export-graphos.md に基づいて回答してください。export を実行した後、ユーザーが実行する rover を使います。
参考資料
first-subgraph.md— 新しいワークスペースで最初に作成するサブグラフ:initからexportまでの直線的な流れ、想定されるゼロ以外の終了コード、初回実行で最もよく遭遇する拒否customer-context.md— インテークが特殊な場合、または不明瞭な場合にのみ参照してください(ステップ 3 で判断します):特殊なワークフロー、不足している入力、調査、質問、context.yamlの要件コントラクトworkspace-contract.md— ファイル、構造、Git の規約connectors-language.md—@source/@connect、式の変数、リレーションシップフィールド、エンティティ、エラー、v0.3 と v0.4 の違いmapping-language.md—selection/body/queryParams:構造、ルーターが実行するメソッド、リテラル、欠落時の扱い、トラブルシューティングnaming.md— 接頭辞、snake_case、セマンティックルート、エイリアスschema-authoring.md— エンベロープ、ページネーション、引数の制約、スカラーの選択、列挙型、null 許容性、深さ、JSON ポリシーspec-intake.md— 記述ドキュメント(OpenAPI 3.x または Swagger 2.0)の読み方、securitySchemesとsecurityがインベントリでどのように扱われるか、仕様書の病理の分類、固定された仕様書とpatches[]api-discovery.md— 仕様書がない場合の手順:ドキュメント、プローブ、出典、確信度、ドキュメントから OAuth フローを記録する方法testing.md— 8 つのレイヤー、それぞれが検出できないこと、落とし穴selection-review.md— UI またはヘッドレスエージェントが選択の変更を提案する際に使用するselection reviewの契約lessons.md— サブグラフ間で共有する知見。ベンダーに依存しないことを学んだら追記してくださいfederation-subgraph.md— ユーザーのグラフのバージョン、コネクターサブグラフに含められるディレクティブと含められないディレクティブ、別のサブグラフが所有するエンティティ、@tagとコントラクト、未解決の項目export-graphos.md— エクスポートするタイミング、コマンドとそのゲート、ユーザーが実行するもの(rover、ルーター)、エクスポートで検証されない内容verification.md— 各レイヤーで証明できること、supergraph_check(実行内容、2 つの変数、pass、fail、not_run の読み方)、引き継ぎ