mcpField
S
storage-creator

storage-creator

github.com/datexデータ・データベース92 回インストール
リポジトリ

storage-creator について

storage-creator は、Datex Studio のブランチでストレージコンポーネントを作成・変更するためのスキルです。スキーマ設計と CLI 検証に加え、関数層からのアクセスやスキーマ変更の影響を扱います。

主な機能

  1. 01ストレージコンポーネントの作成と編集
  2. 02dxs CLI による検証と登録
  3. 03列、型、必須設定の定義
  4. 04必須列の読み込み後パッチ方式
  5. 05スキーマ進化と影響分析の計画
  6. 06関数層でのストレージアクセス

活用シーン

  1. 01機能固有の設定やルールデータの保存
  2. 02ストレージの列設計
  3. 03既存ストレージスキーマの変更
  4. 04関数経由で UI にストレージデータを提供

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/datex/skills --skill storage-creator

インストール後、Claude Code で/storage-creatorを入力して読み込まれたか確認します。

同梱ファイルevals/evals.jsonreferences/storage.md

詳細

ストレージ作成

ブランチ上で、Datex Studio のストレージコンポーネント(configurationTypeId=17)を作成または変更します。ストレージは、機能が所有する、クラウドに永続化される Mongo ベースのコレクションです。ストレージには、Footprint OData スキーマの外部にある状態が保存されます。設定オプション、ルールテーブル、スナップショットの取得データ、派生した分析用集計など、第一級の WMS エンティティではなく「機能独自のデータ」にあたるものです。ストレージ自体はコード文字列を持ちません。関数層で $db.<Package>.<storage_referenceName> ランタイムグローバルを通じて利用します。

参考資料

依存関係

  • requirements-gathering スキル — 会話のコンテキストに要件概要がまだ存在しない場合、その作成のために呼び出されます
  • function-creator スキル — ストレージへの初期データ投入、required: true スキーマでの読み取り後パッチ適用の慣用パターンの記述、またはバックフィルフローの作成時に呼び出されます。$db は関数層専用です。ストレージへの書き込みはすべて関数を通じて行います
  • datasource-creator スキル — UI で利用するためにストレージの読み取りをフロー型データソースでラップする際に呼び出されます(グリッド、セレクター、レポートセクションでストレージデータを表示するための標準パターンです)
  • impact-analysis スキル — 書き込み側の呼び出し元を破損させるおそれのあるスキーマ変更の前に呼び出されます。対象となる変更は、新しい required: true 列の追加、既存列の required: true への変更、列名の変更、呼び出し元がまだ読み取っている可能性のある列の削除です。$db.<Package>.<storage_referenceName> をたどり、読み取り後の値の引き継ぎや名前変更の更新が必要な書き込み箇所をすべて特定します

CLI のライフサイクル

ストレージの作成には dxs configuration を使用します。これは、すべてのプラットフォーム設定タイプを対象とする汎用 CRUD プリミティブです。dxs storage サブコマンドやフィールド単位のパッチ適用はありません。JSON ボディ全体を作成する(または取得して抽出する)必要があります。編集後、全体をプッシュし直します。CLI のタイプ識別子は storage(小文字。ConfigurationEndpoints.normalize_type の出力と一致)で、configurationTypeId: 17 に対応します。

新しいストレージを作成する:

# 1. Build body.json from scratch (see references/storage.md → Minimal Valid Skeleton)
# 2. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate storage -b <branchId> -D body.json
# 3. Create
dxs configuration upsert storage -b <branchId> -D body.json

既存のストレージを編集する:

# 1. Fetch — note the envelope wrapper
dxs configuration get storage <configId> -b <branchId> -O envelope.json
# 2. EXTRACT THE INNER BODY (round-trip footgun guard — see "Round-trip rule" below)
jq .json envelope.json > body.json
# 3. Edit body.json
# 4. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate storage -b <branchId> -D body.json
# 5. Push
dxs configuration upsert storage -b <branchId> -D body.json
ラウンドトリップのルール(重要)

既存の設定を編集する場合、envelope.json を直接 dxs configuration upsert にパイプしてはいけません。設定内容が気付かないうちに失われます。上記の修正済み手順(編集前に jq で内部の .json を抽出する)は、すべてのラウンドトリップで必須です。正式なラウンドトリップ手順と根本的なバグについては、../datex-studio-shared/configuration-roundtrip.md を参照してください。

ストレージの本体は、最もシンプルな設定タイプのひとつです。objectTypeDef[](列スキーマ)、コンポーネント識別情報のエンベロープ、およびほとんどが null のままの少数のトップレベルスロットだけで構成されます。ストレージには独自のコード文字列がないため、inParams/outParams/vars/events/flows の内容はありません。読み取り/書き込みロジックはすべて呼び出し元にあります。ラウンドトリップの手順(取得 → jq で抽出 → 編集 → 検証 → プッシュ)は引き続き適用されます。プラットフォームのインポート処理は、本体のサイズに関係なく、エンベロープを考慮した更新を完全に実行します。

ワークフロー

[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch/connection selection
        |
[requirements brief in context?]
  +-----+-----+
  |            |
 YES          NO -> invoke `requirements-gathering`
  |            |
  +-----+------+
        |
[Phase 2: Decide storage vs Footprint entity]
Consult references/storage.md → Purpose & When to Use:
  - storage: feature-owned data, no Footprint round-trip, no business-
    object lifecycle, no navigation relationships to WMS entities,
    simple column-set with implicit GUID identity
  - first-class Footprint entity: data has business meaning beyond
    the feature (customer, shipment, task), needs OData navigation,
    has CRUD lifecycle reached via actions and OData datasources
If the decision lands on Footprint entity -> stop; this is not a
storage task. Otherwise continue.
        |
[Phase 3: Author storage body]
Build body.json:
  - File shape (configurationTypeId=17, isBlob=false unless intentional,
    suffix -storage.json, referenceName ends _storage, snake_case
    matches filename stem; description non-empty and <=100 chars;
    accessModifier set (default public); feature package, not Utilities)
  - Column descriptors (objectTypeDef[]) — inParam-shaped entries with
    id (snake_case), type (string/number/boolean/date), isCollection
    (false scalar, true array), required defaults to false, isSecured
    typically null; remaining slots null
  - No explicit `id` column — every record carries an implicit
    platform-generated GUID `id` keyed by every $db predicate
  - The `required: true` read-then-patch trap — defaulting `required`
    to false avoids the partial-patch validation footgun; reserve true
    only for columns where every future patch will always echo them;
    when stuck with a legacy required column, write-side code MUST
    read-then-patch (pull row, echo required fields into patch)
  - Additive evolution rules — new nullable columns leave older rows
    with null/undefined unless an explicit backfill runs in the same
    edit; column drops require choosing the legacy-only off-schema
    bracket-notation pattern OR the still-part-of-the-model
    on-schema-required-false pattern based on intent
  - Schema-change impact — for any required:true change, rename, or
    removal, invoke `impact-analysis` skill with target
    $db.<Package>.<storage_referenceName> before push to audit every
    write-side call site
  - Sibling slots — inParams / outParams / vars / events stay null;
    storage has no code strings of its own
        |
[Phase 4: Validate + push]
dxs configuration validate storage -b <branchId> -D body.json
        |
   +----+----+
   |         |
  CREATE   MODIFY-EXISTING
   |         |
   |         use the corrected round-trip
   |         (get -O envelope -> jq .json -> body)
   |         |
   +----+----+
        |
        v
dxs configuration upsert storage -b <branchId> -D body.json
   (upsert creates or updates by referenceName — one command for both)
        |
[Phase 5: Verify in Studio (optional)]
Exercise the storage end-to-end through its callers — seed flow
populates rows, read datasource returns them, write flow applies
the read-then-patch idiom for required columns, schema change
doesn't break existing call sites
        |
[invoke `post-edit-verification`; then `component-validator`]

フェーズの詳細

フェーズ 1:セットアップと要件
  1. ブランチと接続の選択については、../datex-studio-shared/branch-setup.md に従ってください。ブランチ ID を決めつけてはいけません。ユーザーに確認してください。
  2. 会話のコンテキストに要件概要(requirements-gathering または別の呼び出し元スキルが作成したもの)がすでにあるか確認します。
    • 概要がある場合 — それを使用します。概要には、ストレージに保持するデータ(設定値、ビジネスルール、日次スナップショットなど)、型を含む列のセット、保存時に本当に必須となる列の有無、利用する箇所(書き込みを行う関数、UI 用に読み取るデータソース)、およびストレージが属する機能パッケージが記載されている必要があります。
    • 概要がない場合 — まず requirements-gathering スキルを呼び出します。列のセットと required の判断を最初に正しく行うことで、後々の大変なスキーマ移行を避けられます。ストレージをリリースした後では、既存の呼び出し元は新たに必須となった列を追加で反映していないため、required: true は実質的に追加のみとなります。
フェーズ 2:ストレージと Footprint エンティティの選択

作成を始める前に、references/storage.md → 目的と使用する場合 を確認してください。この判断によって、そもそも適切なスキルを使っているかどうかが決まります。

次のようなデータにはストレージが適しています。

  • 機能が所有し、Footprint OData レイヤーを介したラウンドトリップが一切不要です。
  • ファーストクラスの OData エンティティにする必要がありません。WMS エンティティとのナビゲーション関係や、ビジネスオブジェクトのライフサイクル(ステータス遷移や、データ自体を超える監査履歴)はありません。
  • 外部キーを持つ SQL スキーマではなく、暗黙的な GUID 識別情報を持つシンプルな列セットが必要です。
  • $db.<Package>.<storage_referenceName> を介して関数層からのみアクセスされます(ストレージはアクションや UI コンポーネントからはアクセスできません)。

データに機能を超えたビジネス上の意味(顧客、出荷、タスクなど)がある場合は、ファーストクラスの Footprint エンティティが適しています。こうしたデータはプラットフォームの OData スキーマに属し、CRUD アクションと OData データソースを通じてアクセスします。他の WMS エンティティへのナビゲーションプロパティが必要な場合、他の機能からも読み取られる場合、またはアクション駆動の完全な CRUD ライフサイクルが必要な場合は、作業を中止してください。これはストレージのタスクではありません。

判断に迷ったら、次のように考えてください。「このデータは、この機能の動作を支えるためだけに存在するのか、それともプラットフォームの他の部分にも関係するビジネスオブジェクトなのか?」前者はストレージに、後者は Footprint エンティティになります。

フェーズ 3:ストレージ本体の作成

references/storage.md → 最小限の有効なスケルトン のスケルトンから body.json を作成します。主なポイントは次のとおりです。

  1. ファイルの基本事項。 以下の事前チェックリストと../datex-studio-conventions/universal-checklist.mdに従ってください。-storage.jsonファイルの形式については、references/storage.md → ファイルの場所と命名を参照してください。構築時にストレージ固有で確認する点は2つあります。blob形式のストレージを意図している場合を除き、isBlob: falseにすること。また、パッケージの配置はデフォルトのUtilitiesではなく、機能(例:Inventory)に合わせること。これによって、$db.<Package>.<storage_name>のアクセスパスが決まります。

  2. 列の記述子。 各objectTypeDef[]エントリーは、inParam形式です。必須のスロットは、id(列名、snake_case)、type("string"/"number"/"boolean"/"date")、isCollection(スカラー列の場合はfalse、配列列の場合はtrue)です。requiredのデフォルトはfalseです。isSecuredは通常、falseまたはnullです。残りのすべてのスロット(description、oneOf、fromBaseConfiguration、objectTypeDef、objectType、isConstant、constantValue)はnullのままにします。references/storage.md → 列の記述子を参照してください。

  3. id列を明示的に宣言しないでください。 すべてのストレージレコードには、プラットフォームが生成するGUIDのid文字列列が自動的に含まれます。objectTypeDef[]にidを宣言しないでください。これは暗黙的に含まれます。すべての$db述語はこれをキーにします(i => i.id.equals(guid))。明示的なidを宣言すると、暗黙的に含まれるものと衝突し、読み取りが失敗します。

  4. required: trueを設定する場合の読み取り後パッチの落とし穴。 列にrequired: trueを設定すると、$db.update(id, patch)による部分パッチが失敗します。プラットフォームはパッチオブジェクトを宣言済みの列スキーマ全体に対して検証するため、既存レコードに該当フィールドがすでに設定されていても、required: trueの列を省略したパッチは拒否されます。すべての列でrequiredをfalseにすることをデフォルトとし、レコードを書き込むフローレイヤーで検証してください。 required: trueを使うのは、(a)保存時に絶対にnullであってはならず、かつ(b)今後のすべてのパッチで必ず含める列に限ってください。ストレージスキーマに緩和できないrequired: trueの列がすでにある場合、書き込み側のコードでは読み取り後にパッチする必要があります。まず行を取得し、次に必須フィールドを含めたパッチオブジェクトを構築してください。標準的な方法については、references/storage.md → 必須列があるレコードへのパッチを参照してください。

  5. スキーマ変更の影響分析。 次の3つのスキーマ変更は複数箇所に影響するため、プッシュ前に影響分析が必要です。

    • 新しいrequired: true列の追加 — 既存のすべての$db.update呼び出し元でその列を含める必要があります。更新するまで、既存の呼び出し元は検証に失敗しますが、その失敗は表面化しません。
    • 列の名前変更 — 古い名前を参照するすべての読み取り箇所と書き込み箇所が壊れ、生成されるIStorageItem_<Package>_<storage>型の形状も変わります。
    • 過去の行に値が残っている可能性のある列の削除 — Mongoがフィールドを返しても、読み取り箇所の型付きドットアクセスはコンパイルに失敗します。意図に応じて、スキーマ外の角括弧記法パターンか、スキーマ上のrequired: falseパターンを選んでください(トレードオフについてはreferences/storage.md → 列の記述子を参照してください)。

    これらのいずれかに該当する場合は、プッシュ前に対象を$db.<Package>.<storage_referenceName>としてimpact-analysisスキルを実行し、読み取り後のパッチで必須列を含める必要がある箇所や、名前を更新する必要がある箇所をすべて洗い出してください。親エージェント内で呼び出し元をインラインでgrepしないでください。複数ファイルのスキャンは、専用スキルの役目です。

  6. 加算的な進化。 新しいnull許容列を追加しても、既存のレコードにはバックフィルされません。後続のコードでは、古い行のnull/undefinedを許容するか、同じ編集で一度だけ実行するバックフィルフローを用意する必要があります。バックフィルはスキーマ変更と合わせて計画してください。古い行で読み取り側がクラッシュする列をリリースしないでください。

  7. 兄弟スロットは null のままにします。 inParams、outParams、vars、events はすべてストレージのボディで null のままにします。ストレージ自体にはコード文字列がないためです。読み取り/書き込みのロジックはすべて呼び出し元(関数、フロー型データソース)に置きます。この規約に従い、コードスロットを新たに作らないでください。

  8. 呼び出し階層の規約を守ります。 ストレージは $db.<Package>.<storage_referenceName> を介した関数階層専用です。アクションでは利用できず、UI コンポーネント(セレクター、グリッド、フォーム)からも利用できません。UI でストレージデータが必要な場合は、関数を $flows.<Package>.<fn> 経由で呼び出すフロー型データソースで読み取りをラップします。アクションでストレージへの書き込みが必要な場合は、関数で書き込みをラップし、$apis.<Package>.FootprintApi.extendedActions.<function> 経由で呼び出します。../datex-studio-runtime/calling-conventions.md と ../db-query/references/db.md を参照してください。

フェーズ 4:検証してプッシュする
# Validate the body locally against the branch. Exit 1 = validation found errors
# (read validation_errors, fix body.json, re-run) — not a broken CLI. Do not push on exit 1.
dxs configuration validate storage -b <branchId> -D body.json

# For a new storage
dxs configuration upsert storage -b <branchId> -D body.json

# For modify-existing (round-trip — never skip the jq extract)
dxs configuration get storage <configId> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# ... edit body.json ...
dxs configuration upsert storage -b <branchId> -D body.json

検証では、必須フィールドの欠落、列記述子の不正な形式、構造上のエラーをプッシュ前に検出します。一方、required: true による部分パッチの落とし穴(ストレージの検証は問題なく通りますが、実行時エラーは呼び出し元で書き込み時に発生します)、追加的なスキーマ変更でバックフィルが欠落する問題(古い行は null を返し、後続のコードがクラッシュする可能性があります)、呼び出し元へのスキーマ変更の波及(名前変更によって、変更されていないすべての呼び出し箇所が予告なく動作しなくなります)は検出できません。プッシュする前に、references/storage.md → プッシュ前チェックリスト を確認してください(以下のチェックリストはこれに沿ったものです)。

フェーズ 5:Studio で検証する(任意)

呼び出し元を通じて、ストレージをエンドツーエンドでテストします。

  • シード/初期化フローによって、想定される初期行が冪等に設定されます(再実行しても重複しません)。
  • 読み取りフローまたはフロー型データソースが、想定される列の形式で行を返します。述語 DSL の演算子(.equals / .and / .or / .isNull / .in)が記述どおりに機能します。
  • required: true の列を持つ行を対象とする書き込みフローで、読み取り後にパッチを適用するイディオムが正常に機能します。パッチに必須列が含まれ、更新が成功します。
  • スキーマを変更した場合(列の追加/必須設定の変更/名前変更/削除)、監査済みのすべての書き込み側呼び出し箇所が引き続き機能します。検出されない検証エラーや、古いドットアクセスによるコンパイルエラーは発生しません。
  • 暗黙的な id GUID 列がすべての行の読み取り結果に含まれます。この列を基準とする述語は、想定される行を返します。

実行中のアプリを利用できない場合は、設定を再取得し(修正済みの jq .json 抽出パターンを使用)、body.json との差分を確認して、プッシュが反映されたことを確かめます。

プッシュ前チェックリスト

プッシュする前に、references/storage.md → プッシュ前チェックリストのチェックリスト全体を確認してください。簡易版は次のとおりです。

  1. ファイルの基本事項。 configurationTypeId: 17、isBlob: false(意図的に blob モードを使う場合を除く)、末尾が -storage.json、コンポーネント名の末尾が _storage — さらに共通チェック(../datex-studio-conventions/universal-checklist.md)。
  2. パッケージの配置が機能に合っている(例:Inventory)。Utilities には配置しません。これにより $db.<Package>.<storage_name> のアクセスパスが決まります。
  3. 列がすべて定義されている。 objectTypeDef[] の各エントリーに id、type、isCollection が含まれています。未使用のスロットは null のままにします。
  4. 明示的な id 列を作成しない。 プラットフォームが暗黙的な GUID を追加します。
  5. 各列の required のデフォルト値は false。 フロー層で検証してください。将来のすべてのパッチで必ずその列が含まれる場合に限り required: true に設定してください。レガシースキーマですでに required: true になっている場合は、すべての $db.update 呼び出しで読み取り後にパッチを適用し、必須フィールドを含めてください。
  6. スキーマ変更の安全性。 新しい列の場合:呼び出し元が既存の行の null を許容するか、同じ編集でバックフィルを実行してください。required: true の列の変更、名前の変更、または削除の場合:$db.<Package>.<storage_referenceName> に対して impact-analysis スキルを実行し、マージ前にヒットした箇所をすべて監査してください — 親から呼び出し元をインラインで grep してはいけません。
  7. 兄弟スロットは null。 inParams、outParams、vars、events は null のままにします — storage には独自のコード文字列がありません。
  8. コンシューマーが正しい階層にある。 $db の呼び出し元は関数(-flow.json、configurationTypeId: 9)か、フロー型データソース内のフロースロットです。アクションから $db にはアクセスできません。UI コンポーネントからも $db にはアクセスできません。
  9. 述語には流暢な DSL を使う。 .equals(...) / .and(...) / .or(...) / .isNull() / .in([...]) を使用し、===、&&、|| は使わないでください。(TypeScript はネイティブ演算子を問題なく受け入れますが、実行時に述語の変換でエラーが発生するか、ロジックが気づかないうちに崩れます。)

よくある間違い

間違い修正方法
データを第一級の Footprint エンティティにすべきなのに、ストレージを選択したストレージは、機能が所有するデータのうち、ビジネスオブジェクトのライフサイクルを持たず、ナビゲーション関係もないものに使用します。データが顧客/出荷/タスク、または WMS 全体の概念である場合は、Footprint エンティティとしてモデル化し、CRUD アクションと OData データソースを介してアクセスしてください。
objectTypeDef[] で明示的な id 列を宣言した削除してください。プラットフォームはすべてのレコードに暗黙の GUID id 文字列列を追加します。明示的に宣言すると暗黙の列と衝突し、読み取りが失敗します。
すべての列に防御的に required: true を設定した部分パッチのセマンティクスが損なわれます。つまり、$db.update(id, patch) では、1つの列だけを変更する場合でも、すべての必須列を毎回含める必要があります。required の既定値は false とし、フローレイヤーで検証してください。将来のすべてのパッチに必ず含まれる列に限り、true を指定してください。
新しい列を追加した後、$db.update(id, patch) が検証エラーで失敗する新しい列が required: true で、呼び出し元がその列を含めていない(読み取り後にパッチを適用するイディオム)か、パッチがスキーマ全体に対して検証されています。新しい列の required は false を既定とするか、impact-analysis を実行して、すべての書き込み箇所で新しい必須列を含めるように更新してください。
新しい nullable 列を追加したが、古い行の読み取り時にフィールドがある前提の処理が原因でクラッシュする新しい列は既存データに自動補完されません。読み取り箇所で null/undefined を許容するか、同じ編集内で一度だけ実行するバックフィルフローを実行してください。
列を削除したが、更新されていない読み取り箇所がコンパイルに失敗する(宣言されていないフィールドへの型付きドットアクセスがある)意図に応じて選択してください。「この列は不要」→ objectTypeDef[] から削除し、従来の読み取り箇所ではブラケット記法で型なしアクセスを行います(row['retired_field'] as string | null)。「引き続きモデルの一部だが nullable」→ スキーマに required: false として残します。この2つのパターンは意図が異なります。混同しないでください。
ストレージを機能パッケージではなく Utilities の下に配置したパッケージによって $db.<Package>.<storage_name> のアクセスパスが決まります。機能パッケージ(例:Inventory、Acme)の下に配置し、呼び出し元が $db.Utilities.<storage> ではなく $db.Inventory.<storage> としてアクセスできるようにしてください。
description が100文字を超えているSQL 列の上限を超えているため、push 時の検証に失敗します。短くしてください。
referenceName が _storage で終わっていない、またはファイル名のベース名が referenceName と一致しない命名規則から外れています。_storage サフィックスは型を示すもので、_dd/_hub/_form/_editor と同様です。ファイル名のベース名は referenceName と完全に一致させてください。
Predicate でネイティブな TypeScript 演算子(===、&&、`
アクションのコードから $db を使ってストレージを読み取ろうとしている$db は関数層でのみ使用できます。アクションでは利用できず、UI コンポーネントにも公開されていません。読み取りを関数でラップし、アクションから $apis.<Package>.FootprintApi.extendedActions.<function> を介してその関数を呼び出してください。
ストレージ列の名前を変更したが、呼び出し元を更新していない古い名前を参照しているすべての読み取り箇所と書き込み箇所が失敗します。まず $db.<Package>.<storage_referenceName> に対して impact-analysis スキルを実行し、同じ編集内ですべての該当箇所を更新してください。
dxs configuration get -O envelope.json の出力を dxs configuration upsert -D envelope.json に直接渡している設定の内容が警告なしに破壊されます。編集の前に、必ず jq .json envelope.json > body.json を実行してください。上記の「往復ルール」を参照してください。

編集後に post-edit-verification を実行して、description/JSON/スキーマの違反を検出してください。最終レビューでは component-validator を実行してください。

コメント