# Bitwarden Seeder Library - Claude Code Configuration

## Quick Reference

**For detailed pattern descriptions (Factories, Recipes, Models, Scenes, Queries, Data), read `README.md`.**

**For detailed usages of the Seeder library, read `util/SeederUtility/README.md` and `util/SeederApi/README.md`**

## Commands

```bash
# Build
dotnet build util/Seeder/Seeder.csproj

# Run tests
dotnet test test/SeederApi.IntegrationTest/

# Run single test
dotnet test test/SeederApi.IntegrationTest/ --filter "FullyQualifiedName~TestMethodName"
```

## Pattern Decision Tree

```
Need to create test data?
├─ ONE entity with encryption? → Factory
├─ ONE cipher from a SeedVaultItem? → CipherSeed.FromSeedItem() + {Type}CipherSeeder.Create()
├─ MANY entities as cohesive operation? → Recipe or Pipeline
├─ Flexible preset-based seeding? → Pipeline (RecipeBuilder + Steps)
├─ Complete test scenario with ID mangling? → Scene
├─ READ existing seeded data? → Query
└─ Data transformation plaintext ↔ encrypted? → Model
```

## Pipeline Architecture

**Modern pattern for composable fixture-based and generated seeding.**

**Flow**: Preset JSON or Options → RecipeOrchestrator → RecipeBuilder → IStep[] → RecipeExecutor → SeederContext → BulkCommitter

**Key actors**:

- **RecipeBuilder**: Fluent API with dependency validation
- **IStep**: Isolated units of work (CreateOrganizationStep, CreateUsersStep, etc.)
- **SeederContext**: Shared mutable state bag (NOT thread-safe)
- **RecipeExecutor**: Executes steps sequentially, captures statistics, commits via BulkCommitter
- **RecipeOrchestrator**: Orchestrates recipe building and execution (from presets or options)
- **SeederDependencies** (`Options/`): Bundles infrastructure services (`DatabaseContext`, `IMapper`, `IPasswordHasher<User>`, `IManglerService`) into a single record. Recipes and the Orchestrator accept this instead of loose parameters. The CLI utility builds it via `SeederServiceFactory.Create().ToDependencies()`.

**Fixture/preset separation**: Fixtures (organizations, rosters, ciphers) are independent and never reference each other. The preset is the only layer that composes fixtures and defines cross-cutting relationships (folder assignments, favorites). See `Seeds/docs/architecture.md`.

**Phase order (org presets)**: Org → OrgApiKey → Roster → Owner (conditional) → Generator (conditional) → Users → Groups → Collections → Folders → Ciphers → CipherCollections → CipherFolders → CipherFavorites → PersonalCiphers
**Phase order (individual presets)**: IndividualUser → NamedFolders → Generator → Folders → Ciphers → FolderAssignments → FavoriteAssignments

**Individual user presets** use the Pipeline with `CreateIndividualUserStep` (no org, no groups, no collections). These presets live in `Seeds/fixtures/presets/individual/` and are identified by having a `"user"` key instead of `"organization"`. They support `folderNames`, `folderAssignments`, and `favoriteAssignments` for fixture-driven personal vault organization. See `Seeds/docs/presets.md` for the catalog.

See `Pipeline/` folder for implementation.

## Parallelism

Steps execute sequentially (phase order preserved by RecipeExecutor). Within a step, `CreateUsersStep` and `GeneratePersonalCiphersStep` use `Parallel.For` internally for CPU-bound Rust FFI work (key generation, encryption).

**Thread-safety requirements:**

- `GeneratorContext` lazy properties (`??=`) must be force-initialized before any `Parallel.For` loop to prevent a data race
- Generators use `ThreadLocal<Faker>` for thread-safe deterministic data generation
- `ManglerService` and `SeederContext` are NOT thread-safe -- pre-compute their outputs before entering parallel loops

## Performance A/B Testing

When measuring step-level performance changes, use paired worktrees:

- Create `server-PM-XXXXX/perf-baseline` and `server-PM-XXXXX/perf-optimized` worktrees
- Both worktrees get `Stopwatch` timing in `RecipeExecutor.Execute()` (the baseline measurement)
- Only the optimized worktree gets actual code changes
- Run presets with `--mangle` flag to avoid DB collisions between runs
- Compare per-step timings across 3+ runs each, discard the first run (JIT warmup)
- `.worktrees/` is already in `.gitignore`

## Density Profiles

Steps accept an optional `DensityProfile` that controls relationship patterns between users, groups, collections, and ciphers. When null, steps use the original round-robin behavior. When present, steps branch into density-aware algorithms.

**Key files**:

- `Options/DensityProfile.cs` — strongly-typed options (public class)
- `Models/SeedPresetDensity.cs` — JSON preset deserialization targets (internal records)
- `Data/Enums/MembershipDistributionShape.cs` — Uniform, PowerLaw, MegaGroup
- `Data/Enums/CollectionFanOutShape.cs` — Uniform, PowerLaw, FrontLoaded
- `Data/Enums/CipherCollectionSkew.cs` — Uniform, HeavyRight
- `Data/Distributions/PermissionDistributions.cs` — 11 named distributions by org tier

**Backward compatibility contract**: `DensityProfile? == null` MUST produce identical output to the original code. Every step guards this with `if (_density == null) { /* original path */ }`.

**Preset JSON**: Add an optional `"density": { ... }` block. See `Seeds/schemas/preset.schema.json` for the full schema.

**Presets**: Organized into `features/`, `qa/`, `scale/`, `validation/` folders under `Seeds/fixtures/presets/`. See `Seeds/docs/presets.md` for the full catalog.

**Verification**: SQL queries for validating density algorithms are in `Seeds/docs/verification.md`.

## The Recipe Contract

Recipes follow strict rules:

1. A Recipe SHALL accept `SeederDependencies` as its single constructor parameter
2. A Recipe SHALL have exactly one public method named `Seed()`
3. A Recipe MUST produce one cohesive result
4. A Recipe MAY have overloaded `Seed()` methods with different parameters
5. A Recipe SHALL use private helper methods for internal steps
6. A Recipe SHALL use BulkCopy for performance when creating multiple entities
7. A Recipe SHALL compose Factories for individual entity creation
8. A Recipe SHALL NOT expose implementation details as public methods

## Zero-Knowledge Architecture

**Critical:** Unencrypted vault data never leaves the client. The server never sees plaintext.

The Seeder uses the Rust SDK via FFI because it must behave like a real Bitwarden client:

1. Generate encryption keys (like client account setup)
2. Encrypt vault data client-side (same SDK as real clients)
3. Store only encrypted result

## Data Flow

### Pipeline path (fixture → entity)
```
SeedVaultItem → CipherSeed.FromSeedItem() → CipherSeed → {Type}CipherSeeder.Create(options) → CipherViewDto → encrypt_fields (Rust FFI) → EncryptedCipherDto → EncryptedCipherDtoExtensions → Server Cipher Entity
```

### Core encryption (shared by all paths)
```
CipherViewDto → JSON + [EncryptProperty] field paths → encrypt_fields (Rust FFI, bitwarden_crypto) → EncryptedCipherDto → EncryptedCipherDtoExtensions → Server Cipher Entity
```

Shared logic: `Factories/CipherEncryption.cs`, `Models/EncryptedCipherDtoExtensions.cs`

## Rust Crypto Dependency

The Rust shim (`util/RustSdk/rust/`) depends only on `bitwarden_crypto`. It does **not** depend on `bitwarden_vault` — the seeder drives field selection via `[EncryptProperty]` attributes, not SDK cipher types.

Before modifying encryption integration, run `RustSdkCipherTests` to validate roundtrip encryption.

## Deterministic Data Generation

Same domain = same seed = reproducible data:

```csharp
var seed = options.Seed ?? DeriveStableSeed(options.Domain);
```

## Security Reminders

- Default test password: `asdfasdfasdf` (overridable via `--password` CLI flag or `SeederSettings`)
- Never commit database dumps with seeded data
- Seeded keys are for testing only
