# Feature Packs — Overview & Seeder

> All feature packs live in `AdminApp/data/crm/feature-packs/`.
> The `FeaturePackSeeder` applies one pack at a time to the tenant DB.
> See individual pack files for JSON content.

---

## Pack Files

| File | Pack slug | Profile |
|------|-----------|---------|
| `packs/default.md` | `default_crm_pack` | `taskco-crm-general` |
| `packs/education.md` | `education_pack` | `taskco-crm-education` |
| `packs/real-estate.md` | `real_estate_pack` | `taskco-crm-realstate` |
| `packs/pharma.md` | `pharma_pack` | `taskco-crm-pharma` |
| `packs/garments.md` | `garments_pack` | `taskco-crm-garments` |

---

## Pack JSON Structure

Every pack JSON file has this top-level shape:
```json
{
  "slug": "pack_slug",
  "name": "Human Name",
  "version": "1.0.0",
  "entity_domain_maps": [...],
  "custom_fields": { "lead": [...], "deal": [...], "contact": [...] },
  "pipelines": [...],
  "workflow_rules": [...]
}
```

---

## `FeaturePackSeeder` — Risk 4 Mitigation (Additive-Only)

```php
// CoreApp/database/seeders/FeaturePackSeeder.php
namespace CoreApp\Database\Seeders;

use CoreApp\Models\{FieldEngine\CustomField, PipelineEngine\Pipeline,
    PipelineEngine\PipelineStage, WorkflowEngine\WorkflowRule,
    LabelEngine\EntityDomainMap};
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class FeaturePackSeeder extends Seeder
{
    public function run(string $packSlug = 'default_crm_pack'): void
    {
        $path = base_path("AdminApp/data/crm/feature-packs/{$packSlug}.json");

        if (!file_exists($path)) {
            throw new \RuntimeException("Feature pack not found: {$path}");
        }

        $pack = json_decode(file_get_contents($path), true);

        DB::transaction(function () use ($pack) {
            $this->seedEntityDomainMaps($pack);
            $this->seedCustomFields($pack);
            $this->seedPipelines($pack);
            $this->seedWorkflowRules($pack);
        });
    }

    private function seedEntityDomainMaps(array $pack): void
    {
        $profile = $this->profileForPack($pack['slug']);
        foreach ($pack['entity_domain_maps'] ?? [] as $map) {
            EntityDomainMap::updateOrCreate(
                ['product_profile' => $profile, 'entity_type' => $map['entity_type']],
                ['label' => $map['label'], 'label_plural' => $map['label_plural'] ?? null]
            );
        }
    }

    private function seedCustomFields(array $pack): void
    {
        foreach ($pack['custom_fields'] ?? [] as $entityType => $fields) {
            foreach ($fields as $i => $field) {
                CustomField::updateOrCreate(
                    ['slug' => $field['slug']],
                    array_merge($field, [
                        'entity_type'       => $entityType,
                        'is_system'         => true,
                        'feature_pack_slug' => $pack['slug'],
                        'seeder_version'    => $pack['version'],
                        'sort_order'        => $i,
                    ])
                );
            }
        }
    }

    private function seedPipelines(array $pack): void
    {
        foreach ($pack['pipelines'] ?? [] as $pipelineData) {
            $stages = $pipelineData['stages'] ?? [];
            unset($pipelineData['stages']);

            $pipeline = Pipeline::updateOrCreate(
                ['slug' => $pipelineData['slug']],
                array_merge($pipelineData, [
                    'is_system'         => true,
                    'feature_pack_slug' => $pack['slug'],
                    'seeder_version'    => $pack['version'],
                ])
            );

            foreach ($stages as $stage) {
                PipelineStage::updateOrCreate(
                    ['pipeline_id' => $pipeline->id, 'slug' => $stage['slug']],
                    $stage
                );
            }
        }
    }

    private function seedWorkflowRules(array $pack): void
    {
        foreach ($pack['workflow_rules'] ?? [] as $rule) {
            WorkflowRule::updateOrCreate(
                ['slug' => $rule['slug']],
                array_merge($rule, [
                    'is_system'         => true,
                    'feature_pack_slug' => $pack['slug'],
                ])
            );
        }
    }

    private function profileForPack(string $packSlug): string
    {
        return match($packSlug) {
            'education_pack'   => 'taskco-crm-education',
            'real_estate_pack' => 'taskco-crm-realstate',
            'pharma_pack'      => 'taskco-crm-pharma',
            'garments_pack'    => 'taskco-crm-garments',
            default            => 'taskco-crm-general',
        };
    }
}
```

---

## Seeder Rules (Inviolable)

| Rule | Detail |
|------|--------|
| Never delete | No `delete()`, `truncate()`, or `destroy()` calls — ever |
| Never touch user data | `custom_field_values` is never written by seeder |
| Identify system rows | `is_system = true` + `seeder_version` + `feature_pack_slug` |
| Idempotent | Running twice must leave row count unchanged |
| DB transaction | All 4 seed steps wrapped in one transaction |
| `seeder_version` | Update this when pack JSON version bumps — allows future upgrades |
