<!-- このファイルは KrileWorks が公開している Claude Code 用スキルです。
     出典: https://krileworks.com/agent-skills
     手元の .claude/skills/ に置くと、エージェントが Apex Stem の API を
     記憶ではなくこの記述に従って書くようになります。 -->

---
name: apex-blueprint
description: Use this skill when generating test data for Salesforce integration (結合) tests with ApexBlueprint — declaring SObject graphs via SBlueprint.of(...) / .template() / .set() / .alias() / .use() / .after() / .withChildren() / .times() / .owner() / .sharedWith() and realizing them with real DML through SOrchestrator.start(...) / .add() / .create() / .getByAlias(), plus SPersona for runAs test users. Fires on SBlueprint.of, SOrchestrator.start, .alias(), .template(), .use(), .after(), .withChildren(), .times(), .owner(), .sharedWith(), SPersona, ApexBlueprintException, parentIdField, {#}/{P0}/{P1} placeholders.
---

# ApexBlueprint — 結合テストデータ生成リファレンス

結合テスト (Handler 経由・実 DML) のデータを **「最終状態の宣言」** として組む。手続き (insert 順・親 Id 持ち回り) は書かず、`SOrchestrator.create()` が依存解決して realize する。Usecase 単体テストは `MockEloquent`/`MockEntry` 側 (このskill対象外)。

思想 (declarative): テスト本体の **形** がそのままデータ階層・件数・検証意図になる。「知識は DRY (テンプレートに集約)、意図は露出 (テスト本体に直書き)」。

API は **11 メソッド**: `of` / `set` / `template` / `alias` / `use` / `after` / `times` / `withChildren` / `parentIdField` / `owner` / `sharedWith` (+ 別クラス `SPersona`)。

⚠️ **v2.0.0 (2026-07-30) の破壊的変更**: フレームワークの検証エラーは素の `DmlException` ではなく **`ApexBlueprintException`** を投げる (ApexEloquentException と同型の構造化フォーマット)。`create()` の失敗が型で切り分けられる — **`ApexBlueprintException` = 宣言ミス (テストコードを直す) / `DmlException` = org が insert を拒否 (template か org 設定を直す)**。既存の `catch (DmlException)` は捕まらなくなるので移行必須。項目適用エラーは「どの blueprint(alias)・どの経路(set/template/use)・なぜ(存在しない/数式/自動採番/作成不可/型不一致)」まで自動診断される。

---

## SBlueprint — 単一レコードの設計図

`SBlueprint.of(Account.class)` を起点にチェーンで積む。

| メソッド | 役割 |
|---|---|
| `of(Type)` | 起点。SObject タイプを宣言 |
| `set(field, value)` | 単一フィールドに値。同フィールド再呼び出しは **last wins**。template も上書き可 |
| `set(field, value, startAt, interval)` | `{#}` 連番の起点・刻みを指定 (`'Acc-{#}', 10, 2` → Acc-10/12/14) |
| `template(Map<String,Object>)` | デフォルト値を一括適用。RecordTypeId・必須項目・共通値を集約 |
| `alias(name)` | 一意な参照名。`.use()` 参照 / `getByAlias()` 取り出しに使う |
| `alias(name, startAt[, interval])` | `{#}` 入り alias の起点・刻み |
| `use(alias, fromField, toField[, startAt[, interval]])` | 別 blueprint の値を自分に引き写す (後述) |
| `times(n)` | 同 blueprint を n 件量産 (`n<=0` で例外) |
| `after(alias[, startAt[, interval]])` | **順序だけの依存** (v2.0.0+)。値コピーなしで「この alias より後のレイヤーで insert」。トリガー都合の順序制御用。`{#}` パターン可 |
| `owner(user)` | `set('OwnerId', user.Id)` の糖衣 (v2.0.0+)。sharedWith と対で「敵対的データ」を宣言 |
| `sharedWith(user, 'Read'\|'Edit')` | **手動共有を最終状態として宣言** (v2.0.0+)。`Foo__Share`/`AccountShare` の兄弟 blueprint を自動生成し1レイヤー後に insert。`times`/ネスト/`{Pn}` と合成可 (子の共有は子と一緒に増殖)。OWD Public・オーナー自身への共有・不正 accessLevel は fail-fast |
| `withChildren(child)` | 子をネスト。子 lookup に親 Id 自動転記 |
| `parentIdField(field)` | 子が複数 lookup を持つ時、親 Id を入れる項目を明示 |

### `.template()` + `.set()` の分業 (最重要)

共通設定はテンプレート、検証対象だけ `.set()`。`.set()` の行を読むだけで「このテストが何を変えて何を検証するか」が分かる状態を目指す。テンプレートは **単一の `Blueprints` クラスに、オブジェクトごとの `xxxBasic()` メソッド**として集約する (`accBasic()` / `oppBasic()` / `deliveryBasic()` …)。**テンプレートはロジックを持たない単なる `Map` プリセット** — `if` 分岐や条件付き値生成は入れない。シナリオ差はテスト本体の `.set()` で表現する。

```apex
// Blueprints/Blueprints.cls — @isTest にはしない (非テストクラスから利用)。全オブジェクトの基本構成を1クラスに集約
public with sharing class Blueprints {
    /** 法人顧客の基本構成 */
    public static Map<String, Object> accBasic() {
        return new Map<String, Object>{
            'Name' => 'TestAccount',
            'Industry' => 'Technology',
            'AnnualRevenue' => 500000
        };
    }
    /** 商談の基本構成 */
    public static Map<String, Object> oppBasic() {
        return new Map<String, Object>{
            'Name' => 'TestOpp',
            'StageName' => 'Prospecting',
            'CloseDate' => Date.today().addDays(30)
        };
    }
}
```

```apex
SBlueprint.of(Account.class)
    .template(Blueprints.accBasic())
    .set('Name', '○○商事');   // このテストで本筋の項目だけ
```

> 📌 **なぜ「1オブジェクト1クラス」(`AccountBlueprint.basic()`) でなく単一 `Blueprints` クラスか**: オブジェクトごとにクラスを切ると各クラスが Map を返すだけの貧血クラスになり、ファイルが散らばる。単一クラスに `accBasic`/`oppBasic` を並べると **全基本構成が1ファイルに集まり**、**org 側の必須項目追加で結合テストが落ちた時に「どこを直すか」が自明**（該当 `xxxBasic()` の Map に1行足すだけ）。命名は `{オブジェクトの短縮}Basic()`。バリエーションが要れば `oppClosed()` / `accWithParent()` のように接頭辞付きで並べる。

> RecordTypeId のような「全テスト共通だが書き忘れると落ちる」値は必ずテンプレート側に入れる。RecordType ID は `RecordTypeUtil.getRecordTypeIdByDevName(...)` で DevName から動的取得 (ハードコードしない)。

### `.use()` — リレーション作成 と 値コピー

`.use()` は ApexBlueprint で最も多目的な API。1 行で「リレーション作成」と「データコピー」の両方を表す。

```apex
// 用途1: リレーション (Id を子の lookup へ)。親子の insert 順は自動解決
SBlueprint.of(Opportunity.class)
    .set('Name', 'Test Opportunity')
    .set('StageName', 'Prospecting')
    .set('CloseDate', Date.today().addDays(30))
    .use('parentAccount', 'Id', 'AccountId');

// 用途2: 任意フィールドの引き写し (親 Name を子 Description へ)
SBlueprint.of(Contact.class)
    .set('LastName', 'TestContact')
    .use('parentAccount', 'Name', 'Description');
```

---

## SOrchestrator — 依存解決 + 実 DML

| メソッド | 役割 |
|---|---|
| `SOrchestrator.start()` | builder 初期化 (通常用) |
| `SOrchestrator.start(IDmlOperator)` | DML 層差し替え。`new MockDmlOperator()` で実 DML 無し検証 (主に ApexBlueprint 自身のテスト用) |
| `add(blueprint)` | キュー登録。**追加順は無視** (内部でトポロジカルソート) |
| `create()` | 依存解決して正しい順序で insert (最終実行)。**戻り値は `void` — チェーンの末尾に置いて変数に代入するとコンパイルエラー**。`add` の戻りを変数に受けてから別文で呼ぶ |
| `getByAlias(name)` | create 後に alias で生成済レコードを取り出す。戻り値 `SObject` (要キャスト) |

`add` の順は読みやすい順でよい (子を先に書いても解決される)。`getByAlias` でテスト本体に SOQL を書かずアサーションできる。**存在しない alias は `null` を返す** (例外でない) ので typo に注意 → 取り出し直後に `Assert.isNotNull(...)`。`withChildren` した子は自動 alias (`__Account_0_1__...`) になるので、取り出すなら必ず `.alias()` を明示。

```apex
@isTest
static void testInvoke_WhenLinkedToAccount_ThenAccountIdSet() {
    Trace t = Trace.of('正常系: Opportunity が親 Account に紐付いて作成されること');
    t.start();

    SOrchestrator orchestrator = SOrchestrator.start()
        .add(
            SBlueprint.of(Account.class)
                .template(Blueprints.accBasic())
                .alias('parentAccount')
        )
        .add(
            SBlueprint.of(Opportunity.class)
                .set('Name', 'Test Opportunity')
                .set('StageName', 'Prospecting')
                .set('CloseDate', Date.today().addDays(30))
                .use('parentAccount', 'Id', 'AccountId')
                .alias('targetOpp')
        );
    orchestrator.create();   // void なので add チェーンとは別文で呼ぶ

    Account parent = (Account) orchestrator.getByAlias('parentAccount');
    Opportunity opp = (Opportunity) orchestrator.getByAlias('targetOpp');
    Assert.areEqual(parent.Id, opp.AccountId);

    t.finish();
}
```

---

## 親子・量産・参照のパターン

### withChildren — インデント = データ階層

`withChildren` で子をネストすると、子 lookup への親 Id 転記は **自動** (`.use` 不要)。インデント階層がそのままデータ階層になり可読性が高い。単一親子は `.add` 2 本 + `.use` より `withChildren` を優先する。

```apex
SBlueprint.of(Account.class)
    .template(Blueprints.accBasic())
    .alias('acc')
    .withChildren(                                   // 異種の子は withChildren を複数回
        SBlueprint.of(Contact.class)
            .set('LastName', 'Contact-{#}')
            .alias('con_{#}')
            .times(3)                                // 同じ Account に 3 件
    )
    .withChildren(
        SBlueprint.of(Opportunity.class)
            .set('Name', 'TestOpp')
            .set('StageName', 'Prospecting')
            .set('CloseDate', Date.today().addDays(30))
    );
```

ネスト可 (Account > Contact > Case の 3 階層も自然に書ける)。

#### 🧊 イミュータブル (OSS 共通の大前提)

`SBlueprint` を含め **これらの OSS はすべてイミュータブル**。メソッドは自分を書き換えず新インスタンスを返すので、
**戻り値を受け取らない呼び出しは何もしなかったのと同じ**になる (`bp.set('X', v);` を単独文で書いても効かない)。
必ずチェーンするか代入する。イミュータブルでない挙動を見つけたら OSS 側の不具合を疑うこと。
詳細は `apex-eloquent` skill 冒頭の同項を参照。

#### チェーン順: `.alias()` は `.withChildren()` より前に書く

`SBlueprint` は **イミュータブル** で、どのメソッドも `deepCopy()` した新インスタンスを返す。よって
`.alias('acc').withChildren(...)` と `.withChildren(...).alias('acc')` は **どちらも同じ結果になる** (alias は保持される)。
迷わないために **自分の属性 (`template`/`set`/`alias`/`times`) を全部書いてから `withChildren` で子を垂らす** 順に統一する。
子のブロックの後ろに親の `alias` が出てくると、読み手が親子の境目を見失う。

#### 親推論は「型が合う関係が 1 本か」しか見ない

`withChildren` は **親オブジェクトの `getChildRelationships()` を全走査し、子 SObject 型が一致する関係を探す**。

- **2 本以上見つかったら `DmlException`** で即停止する (`create()` 内の analyze 段階。DML には到達しない)。
  > `The child object has multiple parent relationships with the same parent object. object name: Account, parent object name: User`
  → `parentIdField('AccountId')` 等で明示する。**黙って間違った項目を選ぶことはない**。
- **1 本しか無ければ、それが主従でなくても迷わず採用する。ここが罠。**

```apex
// ❌ Estimate__c > SalesOrder__c と積むと…
//    SalesOrder__c から Estimate__c への関係は「複製元」ルックアップ 1 本だけ。
//    推論はそれを埋めて成功し、主従の CustomOpportunity__c が空のまま insert に行く:
//    REQUIRED_FIELD_MISSING, 値を入力してください: [CustomOpportunity__c]
SBlueprint.of(Estimate__c.class).withChildren(SBlueprint.of(SalesOrder__c.class))

// ✅ 木の形は「主従の階層」に一致させる。参照 (lookup) は木にせず alias で結ぶ
SBlueprint.of(CustomOpportunity__c.class)          // 主従の親
    .withChildren(SBlueprint.of(Estimate__c.class).alias('est'))
    .withChildren(SBlueprint.of(SalesOrder__c.class).use('est', 'Id', 'Estimate__c'))
```

**`withChildren` は主従・親子の階層、`use` は横方向の参照** と役割を分ける。
「原価 → 仕入先 (Account)」のように木の一部ではない参照は、その Account を **別の根として `.add()`** し
`use('supplier', 'Id', 'Supplier__c')` で引く。`SOrchestrator` の中なら alias はどの根からでも解決される。

### 自己参照ツリー (同一オブジェクト内の親子) も 1 パスで書ける — raw insert に逃げない

明細行のグループ化 (`EstimateItems__c.ParentItem__c` → 自分と同じオブジェクト) のような **自己参照** は、一見「兄弟同士で Id が要るので Blueprint では無理」に見えるが、**書ける**。レイヤー分割はツリーの深さではなく **依存グラフ駆動** — `use()` のエッジがあれば、同じ親の子同士でも参照される側が先のレイヤーに分離され、Id 確定後に参照する側が insert される。

```apex
// Estimate__c の下に「フラット2行 + グループ1行 + その子2行」を 1 パスで宣言
SBlueprint.of(Estimate__c.class).alias('est')
    .withChildren(SBlueprint.of(EstimateItems__c.class).set('Name', '設計費').set('SortOrder__c', 1))
    .withChildren(
        SBlueprint.of(EstimateItems__c.class)
            .set('Name', '家具工事').set('SortOrder__c', 2)
            .alias('furnitureGroup')                          // ← 参照される側に alias
    )
    .withChildren(
        SBlueprint.of(EstimateItems__c.class)
            .set('Name', '展示什器A').set('SortOrder__c', 3)
            .use('furnitureGroup', 'Id', 'ParentItem__c')     // ← 兄弟の Id を自己参照 lookup へ
    );
```

Estimate__c への lookup は `withChildren` の自動転記、`ParentItem__c` は `use()`、と **2 本の親参照を分担** させるのがコツ。この形を知らないと raw `insert` 直書きに逃げがちだが、直書きは Admin の必須項目追加等で壊れた時に template 中央修正の恩恵の外に出る。

### 既存データへの差分追加 — 第 2 オーケストレータ

「ベースのデータを作る private ヘルパー + テストごとの差分」の形にしたい時、`create()` 済みの SOrchestrator には後から `add()` できない (1 回きり)。差分は **新しい SOrchestrator** で作り、既存レコードへの参照は **具象 Id を `.set()` で直接埋める**。差分バッチ内部の相互参照だけ `use()` を使う:

```apex
Id estimateId = setupEstimate();   // 第1オーケストレータで作った既存データの Id

SOrchestrator delta = SOrchestrator.start()
    .add(
        SBlueprint.of(EstimateItems__c.class)
            .set('Estimate__c', estimateId)                   // 既存レコードへは具象 Id を set
            .set('Name', '家具工事')
            .alias('group')
    )
    .add(
        SBlueprint.of(EstimateItems__c.class)
            .set('Estimate__c', estimateId)
            .set('Name', '展示什器A')
            .use('group', 'Id', 'ParentItem__c')              // 差分バッチ内の参照は use
    );
delta.create();
```

alias は **同一 SOrchestrator 内でしか解決されない** (第1オーケストレータの alias を第2から `use()` はできない)。跨ぐ時は上記のように Id の受け渡しで結ぶ。

### times + プレースホルダ — 連番量産

| プレースホルダ | 展開 (`.set` / `.alias` / `.use` の文字列引数で使用) |
|---|---|
| `{#}` | `1,2,3,...` 数値連番 (`startAt`/`interval` で起点・刻み変更可) |
| `{A}` / `{a}` | `A,B,C` / `a,b,c` アルファベット連番 (人間が判別したいラベル向き) |
| `{P0}` / `{P1}` / ... | **ルートからの絶対深度** で「自分の真の親」を階層解決 |

```apex
SBlueprint.of(Contact.class)
    .set('LastName', 'Contact-{#}')
    .alias('con_{#}')      // con_1 / con_2 / con_3 で個別取り出し可
    .times(3);
```

### Multiplication — 上位 times が下位に伝播

ネスト + `times` は **親ごとに子セットを丸ごと再生成** するので件数は掛け算。`Account.times(2)` × `Contact.times(2)` = Contact 4 件。3 階層各 `times(2)` で孫 8 件。「階層の times 値の積」が末端件数。深いネスト × 大きな times は **DML 行数 10000 を圧迫** するので意識的に絞る。

### 🛡 バルクガバナ結合テストを 1 本 (トリガーカスケードの保険)
トリガーカスケード (請求→売上→見積→商談 のような連鎖) には、**`times` で量産したデータを 1 DML で流し、ガバナ余白を `Assert` する実 DML 結合テストを 1 本** 用意する。**Mock 単体はロジック分岐を網羅できるが実 SOQL/DML を発行しないため、カスケードのクエリ非効率 (段階降り `whereIn` 等) を検出できない**。単一シナリオの IT も 100 SOQL の天井に届かず素通りする。この穴は「量産 × 実 DML × ガバナ assert」でしか塞げない。

```apex
@isTest
static void testCascade_WhenBulk_ThenWithinGovernorLimits() {
  // times で親ごと量産 (例: 商談 201 件それぞれに見積→納品→請求)
  SOrchestrator o = SOrchestrator.start().add(
    SBlueprint.of(Account.class).template(Blueprints.accBasic()).withChildren(
      SBlueprint.of(Opportunity.class).template(Blueprints.oppBasic()).alias('opp_{#}').times(201)
        .withChildren(/* Estimate → Delivery → Invoice … */)));
  o.create();                                // ここは Arrange (update/delete を Act にする場合)
  Test.startTest();
  insert bulkInvoices;                       // Act: カスケードを一括発火
  Integer soqlUsed = Limits.getQueries();    // ★ 必ず「ブロックの中」で掴む
  Test.stopTest();
  // ★ ① 結果の正しさ (1 件でも欠けたら合わない値で縛る。これが主眼)
  Assert.areEqual(402, x01.QuotaQuantity__c, '201 件ぶんが集計されていること');
  // ★ ② 単位ごとの消費 (件数に比例していないか) — apex-trace skill 参照
  TraceFlow.usageOf('...').assertInvocationsAtMost(6).assertSoqlQueriesAtMost(15);
  // ★ ③ 全体のガバナ余白
  Assert.isTrue(soqlUsed < Limits.getLimitQueries() / 2,
    'バルクでも SOQL は上限の半分未満 (親子で畳めているか)。実測 ' + soqlUsed);
}
```
🚨 **`Limits` を `Test.stopTest()` の後で読んではいけない。** `stopTest()` はガバナカウンタを `startTest()` 前に戻すので、**Act ではなく Arrange の値**が返る → `Assert` が**常に真になり何も検証しない** (実測: Act 内 soql=7 → stopTest 後 soql=0)。必ずブロック内で変数に退避する。
- 🚨 **件数は 201 件以上**。Salesforce は**トリガーを 200 件ずつに分けて呼ぶ** (Data Loader のバッチサイズとは別のプラットフォーム挙動)。**200 件までしか流さないと、「1 回の呼び出しで全件が来る」前提の実装や `take(200)`/`LIMIT` を素通りさせる**。実測: Usecase の起動回数は 30件→2回 / 200件→3回 / **201件→5回**。
- ⚠️ **DML 行数 10000 との綱引き**。201 件に子を深くぶら下げると溢れる。**分割を見たいテストは子を最小構成**にし、階層を見たいテストとは分ける (`times` はネストで掛け算になる)。
- ✅ **専用テストを新設せず、既存バルク IT の件数を引き上げる**のが安い (実測: テスト本数据え置き・+19 行・+2.5 秒で、`take(200)` を仕込むと 29 本中そのテストだけが落ちた)。
- ロジック網羅は Mock 単体に任せ、**この 1 本は「本番サイズで結果が欠けない・ガバナに余白がある」を見る**。
- 「大規模データは Util 純粋テストで」は **純関数の上限 (CPU/heap/String)** の話。**トリガーカスケードの SOQL/DML 上限は実 DML でしか出ない**ので、こちらは実 DML バルクで担保する (棲み分け)。
- 💡 **`o.create()` 自体を act にできる**。ApexBlueprint は宣言を実 DML で realize するので、`times(n)` した子の insert がそのまま before/afterInsert をバルク発火させる (「データを作ってから別途 insert して発火」の段取りは不要)。上例のように「セットアップ → 別 DML で発火」が要るのは、**update/delete のカスケードを見たいとき**だけ。
- 💡 `Limits` (同期カスケード全体) に加えて **`TraceFlow.usageOf(name)` で Usecase を名前指定で縛れる** (ApexTrace v1.2.0+)。exclusive 集計なので再入・ネストを二重計上せず、`assertInvocationsAtMost(n)` で**呼ばれた回数** (= 配線の妥当性) も縛れる。🛑 `lastUsage()` は**最後に閉じた 1 本**しか返さないので、ハンドラが Usecase を複数呼ぶバルク IT では使わない。⚠️ **非同期 (バッチ/Queueable) は `Limits` では原理的に測れず (stopTest で初めて走る)、TraceUsage だけが縛れる**。⚠️ `times(2)` 程度では N+1 が浮かない。**`times(201)`** なら N+1 と 200 件分割の両方が同時に浮く。実測ちょうどの値は書かない (上限で書く) — 詳細は `apex-trace` skill。

### use の offset — 不揃いな対応

量産した親の一部だけを子に紐付ける。`.use(alias, from, to, startAt[, interval])`。

```apex
SOrchestrator.start()
    .add(SBlueprint.of(Account.class).alias('acc_{#}')
        .template(Blueprints.accBasic()).times(10))      // acc_1〜acc_10
    .add(SBlueprint.of(Contact.class)
        .set('LastName', 'Con-{#}')
        .alias('con_{#}', 6)                                // con_6〜con_10
        .use('acc_{#}', 'Id', 'AccountId', 6)               // acc_6〜acc_10 を参照
        .times(5));
```

### {Pn} — 「自分の真の親」を参照

`times` で量産した多階層ネストでは、末端から見た真の親は生成ごとに変わる。単純な `{#}` alias だと「Acme-1 下の child_1」と「Acme-2 下の child_1」が alias 重複になる。`{Pn}` は alias を打たずに祖先の値を引き写せる。数え方は **ルートからの絶対深度** (現在地からの相対距離ではない): `{P0}`=ルート、`{P1}`=その 1 つ下。親 Id 転記は `withChildren` 自動処理に任せ、任意フィールドの引き写しだけ `{Pn}` を使う。

```apex
SBlueprint.of(Account.class)                            // P0
    .set('Name', 'Acme-{#}').times(2)
    .withChildren(
        SBlueprint.of(Contact.class)                    // P1
            .parentIdField('AccountId')
            .set('LastName', 'Contact-{#}').times(2)
            .withChildren(
                SBlueprint.of(Case.class)               // P2 (孫)
                    .parentIdField('ContactId')
                    .use('{P1}', 'LastName', 'Subject')  // 自分の真の親 Contact の LastName
                    .times(2)
            )
    );
```

### parentIdField — 複数 lookup の曖昧解消

子が複数 lookup (`AccountId` と `CustomAccount__c` 等) を持つと SOrchestrator がどちらに親 Id を入れるか判断できない → `.parentIdField('AccountId')` で明示。単一 lookup なら不要。「曖昧でエラーになったら付ける」保険。

---

## SPersona — runAs テスト用の制限ユーザー生成 (v2.0.0+)

runAs 監査テスト (apex-access-mode skill) の Arrange 定型を1クラスに集約。**UserFactory を自前で書かない**:

```apex
// プロジェクト側は Personas.cls に集約 (Blueprints.cls とミラー)
User rep = SPersona.of('sales-rep')
  .profile('標準ユーザー')                // 名前解決 + static キャッシュ (Profile 名はロケール依存に注意)
  .permissionSets('InvoiceReadOnly')      // API Name。見つからなければ fail-fast
  .set('LanguageLocaleKey', 'ja')         // 任意 User 項目上書き
  .create();                              // mixed DML を内部で回避 (runAs ラップ) → 通常 DML と混ぜて OK
```

- Username は UUID で並列テスト安全。ロケール系デフォルトは実行ユーザー基準 (org 非依存)
- `create()` はテストコンテキスト専用。毎回新規ユーザー (キャッシュなし)
- **敵対的データの宣言とセットで使う**:

```apex
SOrchestrator.start()
  .add(
    SBlueprint.of(Invoice__c.class).alias('inv')
      .owner(admin)                //  admin 所有
      .sharedWith(rep, 'Read'));   //  rep には Read だけ → 「sharedWith の有無」がそのまま可視性 assert の期待値になる
```

---

## 依存解決の内部 (掘り下げ用)

`create()` は内部で複数問題を 1 パイプラインで解く: ①依存グラフ構築 → ②トポロジカルソート (循環は `Circular or invalid reference detected`) → ③alias 解決 + 自動 alias 払い出し → ④レイヤー単位で realize と DML を **交互ループ**。親レイヤーを insert して Id 確定 → 次レイヤーが `.use(parentAlias,'Id',...)` で本物の Id を受け取る、という同期があるので利用者は親 Id を持ち回らずに済む。`{Pn}` は realize 再帰中の「親位置マップ」で動的解決。

### 主な例外 (すべて create() 時の遅延検出)

| 状況 | 例外型 (v2.0.0+) / メッセージ (抜粋) |
|---|---|
| 循環依存 / 存在しない alias 参照 (typo) | `ApexBlueprintException`: `Circular or invalid reference detected` |
| alias 重複 (同一チェーン内 / `.add` 間) | `ApexBlueprintException`: `Duplicate alias detected` |
| `times(0)` 以下・引数ガード全般 | `ApexBlueprintException` (required / invalidArgument) |
| 複数 lookup で `parentIdField` 未指定 | `ApexBlueprintException`: `multiple parent relationships with the same parent object` |
| 項目適用失敗 (存在しない/数式/自動採番/作成不可/型不一致) | `ApexBlueprintException`: 診断付き (`Reason:` に理由、`Provided:` に値) |
| 必須項目欠落 / バリデーション違反 | **通常の `DmlException` がそのまま** (org が拒否した = データ/org 側の問題) |

---

## 深掘り (元ドキュメント)

- https://krileworks.com/document/ja/apex-blueprint-sblueprint-guide.md — SBlueprint 基本 5 メソッド (of/set/template/alias/use) の解説
- https://krileworks.com/document/ja/apex-blueprint-sorchestrator-guide.md — SOrchestrator の start/add/create/getByAlias とハマりどころ
- https://krileworks.com/document/ja/apex-blueprint-api-sblueprint.md — SBlueprint 全メソッド・全オーバーロード・プレースホルダ・例外の API リファレンス
- https://krileworks.com/document/ja/apex-blueprint-api-sorchestrator.md — SOrchestrator API リファレンス (MockDmlOperator 含む)
- https://krileworks.com/document/ja/apex-blueprint-relations-and-bulk.md — withChildren / times / {#}{A}{a} / use offset / {Pn} / parentIdField の応用パターン集
- https://krileworks.com/document/ja/apex-blueprint-dependency-resolution-deep-dive.md — トポロジカルソート + alias 解決 + レイヤー交互 DML の内部実装フェーズ解説
- https://krileworks.com/document/ja/declarative-data-specification.md — 宣言的データ仕様の思想 (手続き型ファクトリとの対比、「知識は DRY・意図は露出」)
- https://krileworks.com/document/ja/basic-blueprint-building-methods.md — 基本ビルドメソッドの英語寄り別解説 (template クラス例)
- https://krileworks.com/document/ja/create-sobjects-from-blueprints.md — 最小ワークフロー (start→add→create) の入門例
