Files
igny8/docs/plans/final-dev-guides-of-futute-implementation/DocA-SAG-Architecture-Dev-Guide.md
2026-03-08 02:36:44 +00:00

75 KiB
Raw Blame History

Doc A — SAG Architecture: Development Guide for Claude Code

Version: 1.0 Date: March 2026 For: Claude Code (Opus 4.6) in VSCode on IGNY8 repo Purpose: Step-by-step implementation guide for all SAG architecture features — where every file goes, what models to create or modify, what the frontend shows, how automation works Scope: SAG Core (3-layer architecture, Site Builder, Existing Site Intelligence), Interlinking (Doc 3 / Linker module), External Backlink Campaigns (Doc 4), Industry/Sector Templates Rule: Nothing currently working breaks. All new models use nullable fields on existing tables. All new modules use feature flags. All new frontend pages use the existing sidebar/routing pattern.


Table of Contents

  1. System Context — What Exists Today
  2. Implementation Sequence — The Critical Path
  3. Phase 1: Data Foundation — New Models
  4. Phase 2: Sector Attribute Templates
  5. Phase 3: Cluster Formation & Keyword Generation Engine
  6. Phase 4: Case 2 — SAG Site Builder Wizard
  7. Phase 5: Blueprint-Aware Content Pipeline
  8. Phase 6: Taxonomy Creation Flow
  9. Phase 7: Case 1 — Existing Site Analysis
  10. Phase 8: Blueprint Health Monitoring
  11. Phase 9: SAG Interlinking — Linker Module Evolution
  12. Phase 10: External Backlink Campaign Module
  13. Frontend Navigation & UI Map
  14. API Endpoint Registry
  15. AI Function Registry
  16. Celery Task Registry
  17. Feature Flag Registry
  18. Cross-Reference: What Feeds What

1. System Context — What Exists Today

Before building anything, understand the current codebase layout that MUST be preserved.

1.1 Backend File Structure (Django)

backend/igny8_core/
├── auth/                    # User, Account, Site, Sector, Plan models + auth views
│   ├── models.py            # Site model lives here — will get sag_blueprint_id field
│   └── ...
├── api/                     # Base ViewSets, authentication, pagination
├── ai/                      # AI engine — will get new SAG functions
│   ├── engine.py            # AIEngine orchestrator
│   ├── functions/           # AutoCluster, GenerateIdeas, GenerateContent, etc.
│   ├── providers/           # OpenAI, Anthropic, Runware, Bria
│   ├── registry.py          # Function registry
│   └── model_registry.py    # ModelRegistry service
├── modules/                 # API layer — ViewSets, serializers, URLs per module
│   ├── planner/             # Keywords, Clusters, Ideas
│   ├── writer/              # Tasks, Content, Images
│   ├── billing/             # Credits, usage
│   ├── integration/         # WordPress integration
│   ├── system/              # Settings, prompts, AI config
│   ├── linker/              # Internal linking (INACTIVE — behind linker_enabled flag)
│   ├── optimizer/           # Content optimization (INACTIVE)
│   └── publisher/           # Publishing pipeline
├── business/                # Service layer — business logic
│   ├── automation/          # 7-stage automation pipeline
│   ├── content/             # Content generation orchestration
│   ├── integration/         # Sync services
│   ├── linking/             # Link processing (INACTIVE)
│   ├── optimization/        # Content optimization (INACTIVE)
│   ├── planning/            # Clustering, idea generation
│   └── publishing/          # Publishing orchestration
├── plugins/                 # Plugin distribution system
└── tasks/                   # Celery task definitions

1.2 Frontend File Structure (React/TypeScript)

frontend/src/
├── api/                     # API clients (linker.api.ts, optimizer.api.ts, etc.)
├── services/
│   └── api.ts               # Main API service (2500+ lines)
├── store/                   # Zustand stores (authStore, siteStore, sectorStore, billingStore, moduleStore)
├── pages/                   # Route pages
│   ├── Dashboard/           # Main dashboard with widgets
│   ├── Planner/             # Keywords, Clusters, Ideas
│   ├── Writer/              # Tasks, Content, Images, Review, Published
│   ├── Automation/          # Pipeline config and monitoring
│   ├── Linker/              # Internal linking (INACTIVE)
│   ├── Optimizer/           # Content optimization (INACTIVE)
│   ├── Settings/            # Site settings, AI models, prompts
│   ├── Billing/             # Plans, usage, transactions
│   └── Auth/                # Login, register, password reset
├── components/
│   ├── common/              # Shared components
│   ├── dashboard/           # Dashboard widgets
│   └── header/              # NotificationDropdown
├── layout/                  # AppLayout, AppHeader, AppSidebar
└── hooks/                   # Custom hooks

1.3 Current Sidebar Navigation

Dashboard
SETUP
  ├── Add Keywords
  ├── Content Settings
  ├── Sites (if sites_enabled)
  └── Thinker (admin only, if thinker_enabled)
WORKFLOW
  ├── Planner (Keywords → Clusters → Ideas)
  ├── Writer (Queue → Drafts → Images → Review → Published)
  ├── Automation
  ├── Linker (if linker_enabled — currently hidden)
  └── Optimizer (if optimizer_enabled — currently hidden)
ACCOUNT
  ├── Account Settings
  ├── Plans & Billing
  ├── Usage
  └── AI Models (admin only)
HELP
  └── Help & Docs

1.4 Existing Models That Will Be Modified

Model Location What Gets Added Why
Site auth/models.py sag_blueprint_id (FK, nullable) Link site to its active blueprint
Cluster modules/planner/models.py sag_cluster_id (FK, nullable), cluster_type (Enum, nullable) Connect existing clusters to SAG clusters
Tasks modules/writer/models.py sag_cluster_id (FK, nullable), blueprint_context (JSON, nullable) Writer gets cluster context for type-specific prompts
Content modules/writer/models.py sag_cluster_id (FK, nullable), outbound_link_count (Int, nullable), inbound_link_count (Int, nullable), backlink_count (Int, nullable) Map content to clusters, track link metrics
ContentIdea modules/planner/models.py sag_cluster_id (FK, nullable), idea_source (Enum, nullable) Track whether idea came from keyword clustering or SAG blueprint

Critical rule: ALL additions to existing models are nullable. No migrations that alter non-null columns. No breaking changes to existing API serializers — new fields are added as optional with required=False.

1.5 Existing AI Functions

ai/functions/
├── auto_cluster.py          # AutoClusterKeywords — Keywords → Clusters
├── generate_ideas.py        # GenerateContentIdeas — Cluster → Ideas
├── generate_content.py      # GenerateContent — Task → Full article
├── generate_image_prompts.py # GenerateImagePrompts — Content → Image prompts
├── generate_images.py       # GenerateImages — Prompts → Images
└── optimize_content.py      # OptimizeContent — PENDING, not implemented

1.6 Existing Automation Pipeline (7 stages)

Stage 1: Process New Keywords
Stage 2: AI Cluster Keywords
Stage 3: Generate Content Ideas
Stage 4: Create Writer Tasks
Stage 5: Generate Article Content
Stage 6: Extract Image Prompts
Stage 7: Generate Images → Review Queue

1.7 Multi-Tenant Data Scoping

All new models MUST follow the existing pattern:

  • Extend AccountBaseModel for account-scoped data (SAGBlueprint, SAGCampaign)
  • Extend SiteSectorBaseModel for site+sector-scoped data where applicable
  • ViewSets extend AccountModelViewSet or SiteSectorModelViewSet for auto-filtering
  • AccountContextMiddleware resolves tenant from JWT — no manual filtering needed

2. Implementation Sequence — The Critical Path

Everything depends on what came before. This sequence cannot be reordered.

PHASE 1 ─── Data Foundation (NEW models + modified existing models)
   │         No UI yet. Backend only. Migrations.
   │
PHASE 2 ─── Sector Attribute Templates (NEW data layer)
   │         Admin interface to manage templates.
   │         AI-assisted template generation.
   │
PHASE 3 ─── Cluster Formation & Keyword Generation Engine
   │         AI functions that turn attributes → clusters → keywords.
   │         No user-facing UI yet — these are services called by wizard and pipeline.
   │
PHASE 4 ─── SAG Site Builder Wizard (Case 2)
   │         Frontend: Setup wizard gets Step 3 "Site Structure".
   │         First user-facing SAG feature.
   │
PHASE 5 ─── Blueprint-Aware Content Pipeline
   │         Existing 7-stage pipeline enhanced with blueprint context.
   │         Idea generation reads blueprint. Writer uses type-specific prompts.
   │
PHASE 6 ─── Taxonomy Creation Flow
   │         IGNY8 → Plugin: push taxonomy creation payload.
   │         Plugin creates WP taxonomies from blueprint.
   │
PHASE 7 ─── Existing Site Analysis (Case 1)
   │         Plugin sends site data → IGNY8 AI extracts attributes → gap analysis.
   │
PHASE 8 ─── Blueprint Health Monitoring
   │         Background Celery tasks. Dashboard widgets.
   │
PHASE 9 ─── SAG Interlinking (Linker Module Evolution)
   │         Activate linker_enabled. New SAG-aware linking rules.
   │         Depends on: blueprints, clusters, published content.
   │
PHASE 10 ── External Backlink Campaign Module
             Depends on: blueprints, clusters, interlinking, GSC data.
             New models, new UI, FatGrid/PR integrations.

Parallel tracks possible:

  • Phase 2 (templates) and Phase 8 (health monitoring) share no dependencies — template creation can continue while monitoring is built.
  • Phase 9 (interlinking) and Phase 10 (backlinks) can overlap: interlinking rules can be finalized while backlink data models are built.

3. Phase 1: Data Foundation — New Models

3.1 New App: sag

Create a new Django app for all SAG-specific models and business logic. This keeps SAG cleanly separated from existing modules.

Create:

backend/igny8_core/
├── sag/                              # NEW Django app
│   ├── __init__.py
│   ├── apps.py                       # SagConfig
│   ├── models.py                     # SAGBlueprint, SAGAttribute, SAGCluster,
│   │                                 # SectorAttributeTemplate
│   ├── serializers.py                # DRF serializers for all SAG models
│   ├── views.py                      # ViewSets (SAGBlueprintViewSet, etc.)
│   ├── urls.py                       # /api/v1/sag/*
│   ├── admin.py                      # Django admin for SAG models
│   ├── services/                     # Business logic
│   │   ├── __init__.py
│   │   ├── blueprint_service.py      # Blueprint CRUD, versioning, lifecycle
│   │   ├── attribute_service.py      # Attribute framework loading, merging, validation
│   │   ├── cluster_service.py        # Cluster formation, type classification
│   │   ├── keyword_service.py        # Keyword auto-generation from attribute intersections
│   │   ├── template_service.py       # Sector template loading, AI generation
│   │   └── health_service.py         # Blueprint health score calculation
│   ├── ai_functions/                 # SAG-specific AI functions
│   │   ├── __init__.py
│   │   ├── attribute_discovery.py    # AI: Industry+Sector → Attribute framework
│   │   ├── attribute_extraction.py   # AI: Site data → Attribute values (Case 1)
│   │   ├── attribute_population.py   # AI: User inputs → Attribute values (Case 2)
│   │   ├── cluster_formation.py      # AI: Populated attributes → Clusters with types
│   │   ├── keyword_generation.py     # AI: Clusters + attribute values → Keywords
│   │   └── content_planning.py       # AI: Clusters + keywords → Content ideas
│   └── migrations/
│       └── 0001_initial.py

Register in settings.py:

INSTALLED_APPS = [
    ...
    'igny8_core.sag',
]

Register URLs in root urls.py:

urlpatterns = [
    ...
    path('api/v1/sag/', include('igny8_core.sag.urls')),
]

3.2 New Models — Complete Definitions

SAGBlueprint — The master architectural document for a site.

# sag/models.py

class SAGBlueprint(AccountBaseModel):
    """
    The SAG Blueprint is the complete site architecture generated from
    attribute analysis. Both Case 1 (existing site) and Case 2 (new site)
    produce this same model. One active blueprint per site at a time.
    """
    id = models.UUIDField(primary_key=True, default=uuid4)
    site = models.ForeignKey('auth.Site', on_delete=models.CASCADE, related_name='sag_blueprints')
    version = models.IntegerField(default=1)
    status = models.CharField(
        max_length=20,
        choices=[
            ('draft', 'Draft'),           # Generated, awaiting user confirmation
            ('active', 'Active'),         # Confirmed, driving all content operations
            ('evolving', 'Evolving'),     # New clusters being added as site grows
            ('archived', 'Archived'),     # Replaced by newer version
        ],
        default='draft'
    )
    source = models.CharField(
        max_length=20,
        choices=[
            ('site_builder', 'Site Builder (Case 2)'),
            ('site_analysis', 'Existing Site Analysis (Case 1)'),
            ('manual', 'Manual Creation'),
        ]
    )
    # Denormalized JSON for quick reads — authoritative data is in related models
    attributes_json = models.JSONField(default=dict, blank=True)
    clusters_json = models.JSONField(default=dict, blank=True)
    taxonomy_plan = models.JSONField(default=dict, blank=True)
    execution_priority = models.JSONField(default=list, blank=True)
    internal_linking_map = models.JSONField(
        default=dict, blank=True,
        help_text="Placeholder until Doc 3 interlinking spec populates this"
    )
    
    # Metrics
    sag_health_score = models.FloatField(default=0.0, help_text="0-100 score")
    total_clusters = models.IntegerField(default=0)
    total_keywords = models.IntegerField(default=0)
    total_content_planned = models.IntegerField(default=0)
    total_content_published = models.IntegerField(default=0)
    
    # Timestamps
    confirmed_at = models.DateTimeField(null=True, blank=True)
    last_health_check = models.DateTimeField(null=True, blank=True)

    class Meta:
        ordering = ['-created_at']
        unique_together = ['site', 'version']

SAGAttribute — A classification axis within a blueprint.

class SAGAttribute(models.Model):
    """
    One dimensional axis of the SAG grid. Each attribute becomes a WordPress
    custom taxonomy. Values become taxonomy terms.
    """
    id = models.UUIDField(primary_key=True, default=uuid4)
    blueprint = models.ForeignKey(SAGBlueprint, on_delete=models.CASCADE, related_name='attributes')
    name = models.CharField(max_length=100)       # "Target Area"
    slug = models.SlugField(max_length=100)        # "target-area"
    description = models.TextField(blank=True)     # "Primary body area the device targets"
    level = models.CharField(
        max_length=20,
        choices=[
            ('primary', 'Primary'),       # Main navigation axes
            ('secondary', 'Secondary'),   # Cluster depth axes
            ('tertiary', 'Tertiary'),     # Filtering/tagging
        ]
    )
    values = models.JSONField(
        default=list,
        help_text='Array of {name, slug} objects. E.g. [{"name": "Foot", "slug": "foot"}]'
    )
    # WordPress sync state
    wp_taxonomy_slug = models.CharField(max_length=100, null=True, blank=True)
    wp_sync_status = models.CharField(
        max_length=20,
        choices=[
            ('pending', 'Pending'),
            ('synced', 'Synced'),
            ('failed', 'Failed'),
        ],
        default='pending'
    )
    sort_order = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['sort_order', 'level']

SAGCluster — A meaningful intersection of 2+ attribute values.

class SAGCluster(AccountBaseModel):
    """
    A cluster represents a topical ecosystem formed at the intersection of
    2+ attribute values. Each cluster has a hub page and supporting content.
    """
    id = models.UUIDField(primary_key=True, default=uuid4)
    blueprint = models.ForeignKey(SAGBlueprint, on_delete=models.CASCADE, related_name='clusters')
    site = models.ForeignKey('auth.Site', on_delete=models.CASCADE, related_name='sag_clusters')
    name = models.CharField(max_length=200)        # "Foot Massagers for Neuropathy"
    slug = models.SlugField(max_length=200)
    cluster_type = models.CharField(
        max_length=30,
        choices=[
            ('product_category', 'Product/Service Category'),
            ('condition_problem', 'Condition/Problem'),
            ('feature', 'Feature'),
            ('brand', 'Brand'),
            ('informational', 'Informational'),
            ('comparison', 'Comparison'),
        ]
    )
    attribute_intersection = models.JSONField(
        help_text='Which attribute values form this cluster. E.g. {"target_area": "Foot", "relief_focus": "Neuropathy"}'
    )
    
    # Hub page info
    hub_page_title = models.CharField(max_length=300, blank=True)
    hub_page_type = models.CharField(max_length=30, default='cluster_hub')
    hub_page_structure = models.CharField(max_length=30, default='guide_tutorial')
    hub_page_url_slug = models.CharField(max_length=300, blank=True)
    
    # Auto-generated keywords
    auto_generated_keywords = models.JSONField(default=list)
    
    # Supporting content plan
    supporting_content_plan = models.JSONField(
        default=list,
        help_text='Array of {title, type, structure, keywords} for planned supporting content'
    )
    
    # Linking
    linked_attribute_terms = models.JSONField(
        default=list,
        help_text='Which attribute term slugs this cluster connects to'
    )
    cross_cluster_links = models.JSONField(
        default=list,
        help_text='Slugs of other clusters sharing an attribute value'
    )
    
    # Status and metrics
    status = models.CharField(
        max_length=20,
        choices=[
            ('planned', 'Planned'),
            ('partial', 'Partial'),    # Some content published
            ('complete', 'Complete'),  # Hub + all supporting content published
        ],
        default='planned'
    )
    content_count = models.IntegerField(default=0)
    link_coverage_score = models.FloatField(default=0.0, help_text="0-100")
    
    # Backlink campaign fields (Phase 10)
    backlink_target_tier = models.CharField(
        max_length=5,
        choices=[('T1','T1'),('T2','T2'),('T3','T3'),('T4','T4'),('T5','T5')],
        null=True, blank=True,
        help_text='Backlink campaign tier assignment — set by Doc 4 campaign generator'
    )
    
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['name']

SectorAttributeTemplate — The NEW data layer: sector-level attribute intelligence.

class SectorAttributeTemplate(models.Model):
    """
    This is the foundational data layer that does NOT currently exist.
    Each sector gets an attribute framework that defines its dimensional axes
    and suggested values. This is the structural DNA of a sector.
    """
    id = models.UUIDField(primary_key=True, default=uuid4)
    industry = models.CharField(max_length=200)
    sector = models.CharField(max_length=200)
    site_type = models.CharField(
        max_length=30,
        choices=[
            ('e_commerce', 'E-Commerce'),
            ('services', 'Services'),
            ('saas', 'SaaS'),
            ('content', 'Content/Blog'),
            ('local_business', 'Local Business'),
            ('brand', 'Brand'),
        ]
    )
    attribute_framework = models.JSONField(
        help_text='Template attributes with suggested values. Array of {name, description, level, suggested_values[]}'
    )
    keyword_templates = models.JSONField(
        default=dict,
        help_text='Keyword generation templates per cluster type. E.g. {"condition_problem": ["best {target} for {condition}", ...]}'
    )
    source = models.CharField(
        max_length=30,
        choices=[
            ('manual', 'Manual'),
            ('ai_generated', 'AI Generated'),
            ('user_contributed', 'User Contributed'),
        ],
        default='manual'
    )
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ['industry', 'sector']
        ordering = ['industry', 'sector']

3.3 Migrations for Existing Models

Site model (auth/models.py):

# Add to Site model:
sag_blueprint_id = models.ForeignKey(
    'sag.SAGBlueprint', on_delete=models.SET_NULL,
    null=True, blank=True, related_name='active_for_sites',
    help_text='Currently active SAG blueprint for this site'
)

Cluster model (modules/planner/models.py):

# Add to existing Cluster model:
sag_cluster_id = models.ForeignKey(
    'sag.SAGCluster', on_delete=models.SET_NULL,
    null=True, blank=True, related_name='legacy_clusters'
)
cluster_type = models.CharField(max_length=30, null=True, blank=True)

Tasks model (modules/writer/models.py):

# Add to existing Tasks model:
sag_cluster_id = models.ForeignKey(
    'sag.SAGCluster', on_delete=models.SET_NULL,
    null=True, blank=True
)
blueprint_context = models.JSONField(null=True, blank=True)

Content model (modules/writer/models.py):

# Add to existing Content model:
sag_cluster_id = models.ForeignKey(
    'sag.SAGCluster', on_delete=models.SET_NULL,
    null=True, blank=True
)
outbound_link_count = models.IntegerField(null=True, blank=True)
inbound_link_count = models.IntegerField(null=True, blank=True)
backlink_count = models.IntegerField(null=True, blank=True)

ContentIdea model (modules/planner/models.py):

# Add to existing ContentIdea model:
sag_cluster_id = models.ForeignKey(
    'sag.SAGCluster', on_delete=models.SET_NULL,
    null=True, blank=True
)
idea_source = models.CharField(
    max_length=30, null=True, blank=True,
    choices=[
        ('keyword_clustering', 'Keyword Clustering'),
        ('sag_blueprint', 'SAG Blueprint'),
        ('gap_analysis', 'Gap Analysis'),
    ]
)

3.4 Serializers

sag/serializers.py

Create DRF serializers for each model. Key considerations:

  • SAGBlueprintSerializer — include nested attributes and clusters on detail view, exclude on list view for performance
  • SAGBlueprintListSerializer — lightweight: id, site, version, status, health_score, total_clusters, total_keywords, timestamps
  • SAGAttributeSerializer — full attribute with values array
  • SAGClusterSerializer — full cluster with keywords, supporting content plan, linking data
  • SectorAttributeTemplateSerializer — admin-only, includes full attribute_framework JSON

3.5 ViewSets and URLs

# sag/views.py

class SAGBlueprintViewSet(AccountModelViewSet):
    """
    CRUD for SAG Blueprints.
    Additional actions: confirm, archive, regenerate, health_check
    """
    queryset = SAGBlueprint.objects.all()
    serializer_class = SAGBlueprintSerializer
    
    @action(detail=True, methods=['post'])
    def confirm(self, request, pk=None):
        """Move blueprint from draft → active. Sets site.sag_blueprint_id."""
        pass
    
    @action(detail=True, methods=['post'])
    def archive(self, request, pk=None):
        """Move blueprint to archived. Removes from site.sag_blueprint_id."""
        pass
    
    @action(detail=True, methods=['post'])
    def regenerate(self, request, pk=None):
        """Create new version of blueprint from updated attributes."""
        pass
    
    @action(detail=True, methods=['get'])
    def health_check(self, request, pk=None):
        """Run health check and return score + details."""
        pass

class SAGAttributeViewSet(AccountModelViewSet):
    """CRUD for attributes within a blueprint."""
    queryset = SAGAttribute.objects.all()
    serializer_class = SAGAttributeSerializer

class SAGClusterViewSet(AccountModelViewSet):
    """CRUD for clusters within a blueprint."""
    queryset = SAGCluster.objects.all()
    serializer_class = SAGClusterSerializer

class SectorAttributeTemplateViewSet(ModelViewSet):
    """Admin-only: manage sector attribute templates."""
    queryset = SectorAttributeTemplate.objects.all()
    serializer_class = SectorAttributeTemplateSerializer
    permission_classes = [IsAdminUser]
# sag/urls.py

router = DefaultRouter()
router.register(r'blueprints', SAGBlueprintViewSet)
router.register(r'attributes', SAGAttributeViewSet)
router.register(r'clusters', SAGClusterViewSet)
router.register(r'templates', SectorAttributeTemplateViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

3.6 Django Admin

Register all 4 models in Django admin with:

  • SAGBlueprint: list display (site, version, status, health_score, created_at), filters (status, source)
  • SAGAttribute: inline on Blueprint admin, sortable by sort_order
  • SAGCluster: list display (name, cluster_type, status, content_count), filters (cluster_type, status)
  • SectorAttributeTemplate: list display (industry, sector, site_type, source, is_active), filters (industry, site_type, source)

3.7 Frontend — Phase 1 Has No User-Facing UI

Phase 1 is backend-only. The only frontend addition is a read-only blueprint viewer in Site Settings (for development verification):

frontend/src/pages/Settings/
├── SiteSettings.tsx          # EXISTING — add a "SAG Blueprint" tab
└── components/
    └── BlueprintViewer.tsx   # NEW — read-only JSON display of active blueprint

This is a development aid. The real UI comes in Phase 4 (wizard) and Phase 8 (dashboard).


4. Phase 2: Sector Attribute Templates

4.1 What This Phase Delivers

The SectorAttributeTemplate model from Phase 1 gets populated with actual data. This is the structural DNA that makes everything else work.

4.2 Backend: Template Service

sag/services/template_service.py

Functions:

  • get_template(industry, sector) → returns SectorAttributeTemplate or None
  • get_or_generate_template(industry, sector) → returns existing template OR triggers AI generation
  • merge_templates(sector_list) → for multi-sector sites: merge attribute frameworks, combine value lists, deduplicate
  • validate_template(template_data) → ensure all attributes have names, levels, and at least suggested_values structure

4.3 Backend: AI Template Generation

sag/ai_functions/attribute_discovery.py

New AI function: DiscoverSectorAttributes

Registered in AI engine function registry alongside existing functions.

# Register in ai/registry.py:
'discover_sector_attributes': 'igny8_core.sag.ai_functions.attribute_discovery.DiscoverSectorAttributes'

Input: industry name, sector name, site_type Output: attribute_framework JSON matching SectorAttributeTemplate schema Prompt strategy: "You are an expert in site architecture for {industry}/{sector}. Define the dimensional axes that organize this sector. For each attribute, specify: name, description, level (primary/secondary/tertiary), and 5-10 suggested values."

4.4 Backend: Manual Template Seeding

Priority: Seed templates for sectors with highest existing user base in IGNY8. Use the Healthcare/Medical Excel workbook (SAGIndustry01HealthcareMedical.xlsx) as the reference format for how templates should be structured.

Admin Interface: Django admin for SectorAttributeTemplate allows manual creation and editing. The attribute_framework JSON field should use a structured widget or at minimum raw JSON with validation.

4.5 Frontend — Admin Template Manager (Admin Only)

frontend/src/pages/Settings/
└── components/
    └── SectorTemplateManager.tsx   # NEW — admin-only interface

This is a simple CRUD interface visible only to admin users (similar to existing AI Models admin page). Shows list of all templates, allows editing attribute frameworks, and can trigger AI generation for missing sectors.

Not in main sidebar — accessed via Settings → Admin → Sector Templates (similar to how AI Models is nested under Settings).


5. Phase 3: Cluster Formation & Keyword Generation Engine

5.1 What This Phase Delivers

The AI intelligence that turns populated attributes into clusters with types, and clusters into 300-500+ keywords. These are backend services — no direct UI yet. They're called by the wizard (Phase 4) and the pipeline (Phase 5).

5.2 Backend: AI Functions

sag/ai_functions/cluster_formation.pyFormClusters

Register in AI engine:

'form_clusters': 'igny8_core.sag.ai_functions.cluster_formation.FormClusters'

Input: populated attributes (all values per attribute), sector context, site_type Output: array of cluster objects with: name, cluster_type, attribute_intersection, hub_page_title, hub_page_structure, supporting_content_plan

Logic:

  1. Generate all 2-value intersections of primary × primary, primary × secondary
  2. AI evaluates each intersection: is this a real topical ecosystem with search demand?
  3. Valid intersections become clusters
  4. AI classifies each cluster by type (product_category, condition_problem, feature, brand, informational, comparison)
  5. AI generates hub page title and supporting content titles per cluster
  6. Maximum 50 clusters per sector (hard cap from SAG methodology)

sag/ai_functions/keyword_generation.pyGenerateKeywords

Register in AI engine:

'generate_keywords': 'igny8_core.sag.ai_functions.keyword_generation.GenerateKeywords'

Input: clusters with attribute values, keyword_templates from SectorAttributeTemplate Output: per-cluster keyword arrays (15-25 keywords per cluster)

Logic:

  1. Load keyword templates for this sector type
  2. For each cluster, substitute attribute values into templates
  3. Generate long-tail variants with modifiers (best, review, vs, for, how to)
  4. Deduplicate across clusters
  5. Total should be 300-500+ keywords per site

5.3 Backend: Blueprint Assembly Service

sag/services/blueprint_service.py — extend with:

assemble_blueprint(site, attributes, clusters, keywords)

Takes all outputs from Phases 2-3 and assembles the complete SAGBlueprint:

  1. Create SAGBlueprint record (status=draft)
  2. Create SAGAttribute records from attributes
  3. Create SAGCluster records from clusters
  4. Populate auto_generated_keywords on each cluster
  5. Generate taxonomy_plan from attributes
  6. Generate execution_priority from cluster types and keyword volumes
  7. Populate denormalized JSON fields on blueprint
  8. Return blueprint ID

5.4 Backend: Cluster Service

sag/services/cluster_service.py

Functions:

  • classify_cluster_type(intersection, sector_context) → Enum
  • generate_hub_page_info(cluster) → title, type, structure, url_slug
  • plan_supporting_content(cluster) → array of {title, type, structure, keywords}
  • find_cross_cluster_links(all_clusters) → for each cluster, find others sharing attribute values
  • find_linked_attribute_terms(cluster) → which attribute value slugs this cluster connects to

6. Phase 4: SAG Site Builder Wizard (Case 2)

6.1 What This Phase Delivers

The first user-facing SAG feature. The setup wizard gets a new Step 3 "Site Structure" that collects business data and generates a complete blueprint.

6.2 Backend: Wizard API Endpoints

Add to sag/views.py or create sag/wizard_views.py:

# POST /api/v1/sag/wizard/generate-attributes/
# Input: {industry, sectors[], site_type}
# Output: {attributes: [{name, level, suggested_values}]}
# Calls: template_service.get_or_generate_template() + merge if multi-sector

# POST /api/v1/sag/wizard/populate-attributes/
# Input: {attributes: [{name, level, values}], business_data: {products, services, brands, locations, conditions}}
# Output: {populated_attributes: [{name, level, values}]}  (values now enriched from business data)
# Calls: ai_functions.attribute_population.PopulateAttributes

# POST /api/v1/sag/wizard/generate-blueprint/
# Input: {site_id, populated_attributes, mode: 'quick'|'detailed'}
# Output: {blueprint_id, clusters, keywords_count, content_plan_count, taxonomy_count}
# Calls: cluster_formation → keyword_generation → blueprint_service.assemble_blueprint()

# POST /api/v1/sag/wizard/confirm-blueprint/
# Input: {blueprint_id}
# Output: {success, blueprint_status: 'active'}
# Calls: blueprint_service.confirm() — sets site.sag_blueprint_id, triggers taxonomy creation

6.3 Backend: AI Function for Business Data Population

sag/ai_functions/attribute_population.pyPopulateAttributes

Input: attribute framework + user's business data (products list, services list, brands, locations, conditions) Output: populated attributes with site-specific values extracted from business data

Logic:

  1. Parse business data inputs (free text, comma-separated lists, structured inputs)
  2. Map business items to attribute values (e.g., "foot massagers, neck massagers" → Target Area: [Foot, Neck])
  3. Identify values not in suggested_values → add as new values
  4. Apply pruning rules (single-value primary → demote to secondary, etc.)
  5. Assess completeness per attribute → flag any that need user attention

6.4 Frontend: Setup Wizard Enhancement

The existing setup wizard lives at:

frontend/src/pages/Settings/    # or wherever the Add Site wizard is

Modify the wizard to insert Step 3 between "Add Site" and "Connect WordPress":

frontend/src/pages/Settings/
├── SetupWizard/                       # EXISTING wizard component
│   ├── WizardStep1Welcome.tsx         # No changes
│   ├── WizardStep2AddSite.tsx         # No changes — already collects Industry, Sectors
│   ├── WizardStep3SiteStructure.tsx   # NEW — the Site Builder step
│   │   ├── ModeSelector.tsx           # Quick Mode vs Detailed Mode toggle
│   │   ├── AttributeReview.tsx        # Shows AI-generated attributes, toggle on/off, edit values
│   │   ├── BusinessDataInput.tsx      # Per-site-type input forms (products, services, etc.)
│   │   ├── BlueprintPreview.tsx       # Tree view of clusters, keywords count, content plan
│   │   └── BlueprintConfirm.tsx       # Approve & Build / Adjust Attributes buttons
│   ├── WizardStep4ConnectWP.tsx       # EXISTING — enhanced: auto-creates taxonomies after connect
│   ├── WizardStep5Keywords.tsx        # EXISTING — now shows "optional" messaging + gap analysis
│   └── WizardStep6Complete.tsx        # EXISTING — enhanced: shows blueprint summary

6.5 Frontend: Step 3 Component Details

ModeSelector.tsx

  • Two cards: "Quick Mode" (auto-build, skip review) and "Detailed Mode" (full review + business data)
  • Quick mode calls: generate-attributes → generate-blueprint (with defaults) → confirm
  • Detailed mode proceeds through all sub-steps

AttributeReview.tsx

  • Displays attributes grouped by level (Primary, Secondary, Tertiary)
  • Each attribute: name, toggle switch (on/off), expandable values list
  • Values: chip/tag UI with [+ Add] button, click to remove
  • [+ Add Custom Attribute] button at bottom
  • Loading state while AI generates attributes

BusinessDataInput.tsx

  • Dynamic form based on site_type from Step 2
  • E-commerce: Products/categories (textarea), Device types (textarea), Problems solved (textarea), Brands (textarea), Features (textarea)
  • Services: Services offered (textarea), Locations (textarea), Property types (textarea), Problems solved (textarea)
  • SaaS: Features (textarea), Use cases (textarea), Integrations (textarea), Competitors (textarea)
  • Content: Topics (textarea), Subtopics (textarea), Audience (textarea)
  • Local Business: Services (textarea), Locations (textarea), Conditions (textarea)
  • Each textarea has helper text: "Enter one per line, or comma-separated"

BlueprintPreview.tsx

  • Tree/accordion view of clusters grouped by primary attribute value
  • Each cluster shows: name, type badge, hub page title, supporting content count, keyword count
  • Summary bar at top: X clusters, Y keywords, Z content pieces, W taxonomies
  • Expandable per cluster: see all keywords, all planned supporting content titles
  • This is the "Your Site Architecture" preview from the spec document

BlueprintConfirm.tsx

  • Two buttons: [Approve & Build] and [Adjust Attributes] (goes back to AttributeReview)
  • Approve triggers: POST /wizard/confirm-blueprint/
  • Shows brief loading animation during confirmation
  • On success: proceeds to Step 4 (Connect WordPress)

6.6 Frontend: Zustand Store

frontend/src/store/
└── sagStore.ts              # NEW
interface SAGState {
  blueprint: SAGBlueprint | null;
  attributes: SAGAttribute[];
  clusters: SAGCluster[];
  wizardMode: 'quick' | 'detailed';
  wizardStep: number;       // Sub-step within Step 3
  isGenerating: boolean;
  error: string | null;
  
  // Actions
  setWizardMode: (mode: 'quick' | 'detailed') => void;
  generateAttributes: (industry: string, sectors: string[]) => Promise<void>;
  populateAttributes: (attributes: any[], businessData: any) => Promise<void>;
  generateBlueprint: (siteId: string) => Promise<void>;
  confirmBlueprint: (blueprintId: string) => Promise<void>;
  loadBlueprint: (siteId: string) => Promise<void>;
}

6.7 Frontend: API Client

frontend/src/api/
└── sag.api.ts               # NEW

API client for all /api/v1/sag/* endpoints. Follows existing pattern from other API clients (linker.api.ts, optimizer.api.ts).


7. Phase 5: Blueprint-Aware Content Pipeline

7.1 What This Phase Delivers

The existing 7-stage automation pipeline becomes SAG-aware. Ideas generate from the blueprint, tasks carry cluster context, writer uses type-specific prompts, and content gets auto-assigned to correct taxonomies.

7.2 Backend: Modified Automation Stages

Location: business/automation/ — modify existing stage handlers

Stage 0 (NEW): Blueprint Check

# business/automation/stages/stage_0_blueprint_check.py  (NEW)
# Before any pipeline run:
# 1. Check if site has active SAG blueprint (site.sag_blueprint_id is not None)
# 2. If yes: load blueprint, identify unfulfilled content needs
# 3. If no: pipeline runs in legacy mode (no changes to current behavior)

Stage 2: AI Cluster Keywords — ENHANCED

# business/automation/stages/ — modify existing Stage 2
# If blueprint exists:
#   - Skip AI clustering (clusters already defined by blueprint)
#   - Instead: map any new user-added keywords to existing SAG clusters
#   - Flag keywords that don't match any cluster
# If no blueprint:
#   - Run existing AutoClusterKeywords (unchanged)

Stage 3: Generate Content Ideas — ENHANCED

# business/automation/stages/ — modify existing Stage 3
# If blueprint exists:
#   - Call sag/ai_functions/content_planning.py instead of generic idea generation
#   - Each idea gets: correct Sector, Structure, Type, Cluster from blueprint
#   - idea_source = 'sag_blueprint'
# If no blueprint:
#   - Run existing GenerateContentIdeas (unchanged)

Stage 4: Create Writer Tasks — ENHANCED

# business/automation/stages/ — modify existing Stage 4
# If blueprint exists:
#   - Tasks carry sag_cluster_id and blueprint_context
#   - blueprint_context includes: cluster_type, hub_title, attribute_terms, related_content
# If no blueprint:
#   - Run existing task creation (unchanged)

Stage 5: Generate Article Content — ENHANCED

# business/automation/stages/ — modify existing Stage 5
# If task has blueprint_context:
#   - Select prompt template by Structure + Type (instead of generic)
#   - Include SAG context variables: {cluster_name}, {cluster_type}, {hub_title}, {attribute_terms}
#   - Content generation includes type-specific sections
# If no blueprint_context:
#   - Run existing GenerateContent (unchanged)

Stage 6 (NEW enhancement): Taxonomy Assignment

# After content generation, before image prompts:
# If blueprint exists:
#   - Auto-assign content to correct custom taxonomies from blueprint
#   - Set sag_cluster_id on Content record
#   - Mark cluster status as 'partial' if first content piece

7.3 Backend: Enhanced Prompt Templates

Location: Existing PromptTemplate model or defaults in code

Add SAG-context prompt variants. These are stored in the same PromptTemplate system but selected based on content type:

Content Type Structure Prompt Template Key
Cluster Hub Page Guide Tutorial sag_hub_guide
Cluster Hub Page Top Listicle sag_hub_listicle
Blog Post Comparison sag_blog_comparison
Blog Post Review sag_blog_review
Blog Post How To sag_blog_howto
Blog Post Question sag_blog_question
Attribute Term Page Guide Tutorial sag_term_page
Product Page Review sag_product_page
Service Page Guide Tutorial sag_service_page

Each prompt includes SAG context variables:

You are writing a {content_type} for the cluster "{cluster_name}".
This cluster covers the intersection of {attribute_intersection}.
The hub page for this cluster is: "{hub_page_title}" at {hub_page_url}.
Related content in this cluster includes: {related_blogs}.
This content should naturally reference these taxonomy terms: {attribute_terms}.

7.4 Frontend: Pipeline UI Shows Blueprint Context

Location: pages/Automation/ — existing pipeline monitoring view

When a site has an active blueprint, the automation page shows:

  • Blueprint status badge: "SAG Blueprint Active (v1)"
  • Pipeline mode indicator: "Running in SAG Mode" vs "Running in Legacy Mode"
  • Stage 0 (Blueprint Check) appears as the first stage in the progress display

Location: pages/Writer/ — existing task and content views

When viewing a task or content item that has sag_cluster_id:

  • Show cluster badge: "Cluster: Foot Massagers for Neuropathy"
  • Show type badge: "Type: Hub Page" or "Type: Supporting Blog"
  • Show blueprint context panel (collapsible): cluster type, attribute terms, related content links

These are display-only enhancements to existing views — no new pages needed.


8. Phase 6: Taxonomy Creation Flow

8.1 What This Phase Delivers

When a blueprint is confirmed, IGNY8 pushes taxonomy creation instructions to the WordPress plugin, which creates the actual WordPress custom taxonomies and terms.

8.2 Backend: Taxonomy Payload Generator

sag/services/taxonomy_service.py    # NEW

Functions:

  • generate_taxonomy_payload(blueprint) → JSON payload matching plugin's expected format
  • send_taxonomy_payload(site, payload) → POST to WordPress plugin endpoint
  • handle_taxonomy_response(site, response) → update SAGAttribute wp_sync_status
  • check_taxonomy_status(site) → GET current taxonomy state from plugin

Payload format:

{
  "taxonomies": [
    {
      "slug": "target-area",
      "label": "Target Area",
      "description": "Primary body area the device targets",
      "hierarchical": true,
      "post_types": ["product", "post", "page"],
      "terms": [
        {"name": "Foot", "slug": "foot", "description": "Foot massagers and foot therapy devices"},
        {"name": "Neck", "slug": "neck", "description": "Neck massagers and cervical therapy"}
      ]
    }
  ]
}

8.3 Plugin Side: Taxonomy Builder

The WordPress plugin already has planned files for this:

igny8-plugin/includes/modules/sag/
├── class-taxonomy-builder.php     # Create WP taxonomies from payload
├── class-term-builder.php         # Create terms within taxonomies

Plugin endpoint: POST /wp-json/igny8/v1/sag/create-taxonomies

  • Receives payload from IGNY8 app
  • Registers taxonomies via register_taxonomy()
  • Creates terms via wp_insert_term()
  • Returns success with term IDs mapped to slugs
  • Stores taxonomy registrations in option for persistence across requests

8.4 Backend: Integration with Existing Sync Architecture

Location: business/integration/ — extend existing sync services

The taxonomy creation uses the same Site.wp_api_key authentication and webhook architecture that content publishing already uses. No new auth mechanism needed.

Flow:

Blueprint confirmed
  → sag/services/taxonomy_service.generate_taxonomy_payload()
  → business/integration/sync_service.push_to_wordpress(site, '/sag/create-taxonomies', payload)
  → Plugin creates taxonomies + terms
  → Plugin POSTs response back to IGNY8 webhook
  → sag/services/taxonomy_service.handle_taxonomy_response()
  → SAGAttribute.wp_sync_status updated to 'synced'

8.5 Frontend: Taxonomy Sync Status

Location: pages/Settings/SiteSettings.tsx — add SAG tab or extend existing integration tab

Show:

  • List of attributes from blueprint
  • Each attribute: name, WP taxonomy slug, sync status (pending/synced/failed)
  • "Sync Taxonomies" button to retry failed syncs
  • This is visible after blueprint is confirmed (Phase 4) and site is connected to WordPress

9. Phase 7: Existing Site Analysis (Case 1)

9.1 What This Phase Delivers

For existing WordPress/WooCommerce sites, IGNY8 collects site data, AI extracts attributes from it, generates a gap analysis, and produces a blueprint.

9.2 Backend: Extended Site Data Collection

Location: Plugin already has site data collection:

igny8-plugin/data/site-collection.php    # Collects post data, taxonomy data, product data
igny8-plugin/data/semantic-mapping.php   # Maps site content to semantic strategy

Enhance plugin to collect additional data for SAG:

  • All WooCommerce product attributes + values (pa_color, pa_size, custom attributes)
  • Product titles and descriptions (for AI attribute extraction)
  • Existing custom taxonomies with all terms
  • Navigation menu structure with hierarchy
  • Page templates in use

New plugin endpoint: GET /wp-json/igny8/v1/sag/site-analysis Returns comprehensive site data package for AI analysis.

9.3 Backend: AI Attribute Extraction (Case 1)

sag/ai_functions/attribute_extraction.pyExtractAttributes

Input: raw site data (product titles, descriptions, categories, existing taxonomies, menu structure) Output: discovered attributes with values and confidence scores

Logic:

  1. AI analyzes product/content titles for recurring patterns
  2. Cross-references against sector template (if exists) for validation
  3. Returns discovered attributes with frequency counts per value
  4. Flags low-confidence discoveries for user review

9.4 Backend: Gap Analysis Service

sag/services/gap_analysis_service.py    # NEW

Functions:

  • analyze_gap(site_data, blueprint) → comprehensive gap report
  • Gap report includes: missing cluster hubs, missing supporting content, products needing attribute enrichment, taxonomy terms without content, internal links missing

9.5 Frontend: Case 1 Analysis UI

Location: Accessed from Site Settings when a site is connected but has no blueprint

frontend/src/pages/Settings/
└── components/
    ├── SiteAnalysis.tsx          # NEW — trigger and display site analysis
    ├── DiscoveredAttributes.tsx  # NEW — show AI-discovered attributes, user confirms/edits
    ├── GapAnalysisReport.tsx     # NEW — show gap analysis results
    └── ProductAutoTagger.tsx     # NEW — review and approve AI-generated product tags

Flow:

  1. User clicks "Analyze Site Structure" in Site Settings
  2. Loading state while plugin collects data and AI processes it
  3. DiscoveredAttributes screen: shows AI findings, user toggles/edits/confirms
  4. After confirmation → blueprint generates (same as Case 2 but with extracted data)
  5. GapAnalysisReport: shows what's missing vs what the blueprint recommends
  6. ProductAutoTagger: for e-commerce sites, review AI-suggested attribute assignments per product

10. Phase 8: Blueprint Health Monitoring

10.1 What This Phase Delivers

Background monitoring that tracks blueprint completion, content coverage per cluster, link health, and overall SAG health score. Dashboard widgets surface this data.

10.2 Backend: Health Service

sag/services/health_service.py    # Already created in Phase 1, now implement

calculate_health_score(blueprint) returns 0-100 score based on:

  • Content completeness per cluster (hub exists? blogs published?) — 30 points
  • Taxonomy sync status (all taxonomies created?) — 15 points
  • Internal link coverage (from Linker module when active) — 20 points
  • Keyword coverage (keywords have matching content?) — 15 points
  • Unassigned content (pages/products not mapped to clusters) — 10 points
  • Overall structure balance (no cluster with 0 content while others have 5+) — 10 points

10.3 Backend: Celery Tasks

sag/tasks.py    # NEW
@shared_task
def run_blueprint_health_check(site_id):
    """Weekly health check for a site's active blueprint."""
    site = Site.objects.get(id=site_id)
    if not site.sag_blueprint_id:
        return
    blueprint = site.sag_blueprint
    score = health_service.calculate_health_score(blueprint)
    blueprint.sag_health_score = score
    blueprint.last_health_check = now()
    blueprint.save()

@shared_task
def check_blueprint_evolution_triggers(site_id):
    """Check if blueprint needs evolution (new products, new keywords, etc.)."""
    pass

Register in Celery Beat schedule (existing celery.py configuration):

  • run_blueprint_health_check: weekly for all sites with active blueprints
  • check_blueprint_evolution_triggers: weekly, same schedule

10.4 Frontend: Dashboard Widgets

Location: components/dashboard/ — add new widgets alongside existing ones

frontend/src/components/dashboard/
├── SAGHealthWidget.tsx          # NEW — health score gauge, cluster completion bars
├── BlueprintProgressWidget.tsx  # NEW — content published vs planned per cluster

SAGHealthWidget:

  • Circular gauge showing overall health score (0-100)
  • Color: green (80+), yellow (50-79), red (<50)
  • Below gauge: quick stats (clusters complete, content published/planned, taxonomies synced)
  • Click → goes to blueprint detail page

BlueprintProgressWidget:

  • Horizontal bar chart: each cluster as a row
  • Bar shows: published content / planned content
  • Color coding by cluster status: complete (green), partial (yellow), planned (gray)
  • Sorted by execution priority

Dashboard integration:

  • Widgets only appear when site has an active blueprint
  • Add to existing dashboard grid alongside WorkflowPipelineWidget, AIOperationsWidget, etc.

10.5 Frontend: Blueprint Management Page

frontend/src/pages/
└── Blueprint/                    # NEW page
    ├── BlueprintOverview.tsx     # Health score, cluster grid, quick actions
    ├── ClusterDetail.tsx         # Single cluster view: hub, supporting content, keywords, links
    ├── AttributeManager.tsx      # View/edit attributes, see taxonomy sync status
    └── ExecutionPlan.tsx         # Phase 1-4 execution plan, what to build next

Sidebar addition:

WORKFLOW
  ├── Planner
  ├── Writer
  ├── Automation
  ├── Blueprint (NEW — if site has active blueprint, always visible)
  ├── Linker (if linker_enabled)
  └── Optimizer (if optimizer_enabled)

11. Phase 9: SAG Interlinking — Linker Module Evolution

11.1 What This Phase Delivers

The existing Linker module (currently INACTIVE behind linker_enabled flag) evolves from generic content-similarity linking to SAG-aware deterministic linking. This implements Doc 3 — Interlinking Specification.

11.2 Backend: New Models for Interlinking

Add to sag/models.py:

class SAGLink(AccountBaseModel):
    """Individual internal link tracked by the SAG interlinking system."""
    id = models.UUIDField(primary_key=True, default=uuid4)
    blueprint = models.ForeignKey(SAGBlueprint, on_delete=models.CASCADE, related_name='links')
    source_content = models.ForeignKey('writer.Content', on_delete=models.CASCADE, related_name='outbound_sag_links')
    target_content = models.ForeignKey('writer.Content', on_delete=models.CASCADE, null=True, blank=True, related_name='inbound_sag_links')
    target_url = models.URLField(blank=True, help_text='For term pages or external targets')
    link_type = models.CharField(max_length=30, choices=[
        ('hub_upward', 'Supporting → Hub'),
        ('hub_downward', 'Hub → Supporting'),
        ('sibling', 'Sibling within cluster'),
        ('cross_cluster', 'Cross-cluster hub ↔ hub'),
        ('taxonomy_contextual', 'Term page → cluster hubs'),
        ('breadcrumb', 'Breadcrumb structural'),
        ('related_content', 'Related reading cross-cluster'),
    ])
    anchor_text = models.CharField(max_length=300)
    placement_zone = models.CharField(max_length=30, choices=[
        ('in_body', 'In Body'),
        ('related_section', 'Related Section'),
        ('breadcrumb', 'Breadcrumb'),
        ('guide_contents', 'Guide Contents'),
    ])
    status = models.CharField(max_length=20, choices=[
        ('planned', 'Planned'),
        ('injected', 'Injected into content'),
        ('published', 'Published on WordPress'),
        ('broken', 'Broken'),
        ('removed', 'Removed'),
    ], default='planned')
    score = models.FloatField(default=0.0)
    injected_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

class SAGLinkAudit(AccountBaseModel):
    """Periodic link audit snapshot."""
    id = models.UUIDField(primary_key=True, default=uuid4)
    blueprint = models.ForeignKey(SAGBlueprint, on_delete=models.CASCADE)
    audit_date = models.DateTimeField(auto_now_add=True)
    total_links = models.IntegerField(default=0)
    missing_mandatory = models.IntegerField(default=0)
    orphan_pages = models.IntegerField(default=0)
    broken_links = models.IntegerField(default=0)
    over_linked_pages = models.IntegerField(default=0)
    cluster_scores = models.JSONField(default=dict)
    recommendations = models.JSONField(default=list)

11.3 Backend: Linking Services

sag/services/linking_service.py    # NEW

Functions:

  • generate_links_for_content(content) → creates SAGLink records for a newly generated content piece
  • audit_cluster_links(cluster) → score the cluster's link completeness (0-100)
  • audit_site_links(blueprint) → full site link audit
  • find_missing_mandatory_links(blueprint) → list of mandatory links that don't exist
  • suggest_anchor_text(source_content, target_content, link_type) → AI-generated anchor text variants

Link generation rules (from Doc 3):

Link Type Rule Mandatory? Max Count
hub_upward Every supporting blog → its cluster hub YES 1 per blog
hub_downward Every hub → all its published supporting content YES All
sibling Supporting ↔ supporting within same cluster No 2 per article
cross_cluster Hub ↔ hub sharing attribute value No 2 per hub
taxonomy_contextual Term page → all hubs using that value YES All
breadcrumb Every page has breadcrumb path YES 1 chain
related_content Supporting → content in other clusters No 2-3

11.4 Backend: Pipeline Integration (Stage 8)

Add Stage 8 to automation pipeline:

# business/automation/stages/stage_8_link_generation.py    # NEW

# After Stage 7 (images generated):
# 1. Load blueprint context for this content
# 2. Generate required SAGLink records per rules
# 3. Inject link HTML into content body
# 4. Content enters review queue with links already present

Modify business/automation/pipeline.py to include Stage 8 when linker_enabled AND blueprint exists.

11.5 Frontend: Linker Page Evolution

Location: pages/Linker/ — existing but INACTIVE

When linker_enabled is activated and blueprint exists, the Linker page shows:

frontend/src/pages/Linker/
├── LinkerOverview.tsx         # REPLACE existing — SAG link dashboard
│   ├── ClusterLinkCoverage.tsx  # Table: clusters × link scores
│   ├── SiteLinkMap.tsx          # Visual: clusters as nodes, links as edges
│   ├── LinkAuditDashboard.tsx   # Stats: total links, missing, broken, orphans
│   └── GapAnalysisExport.tsx    # Download link gap report
├── LinkDetail.tsx             # Single link view: source, target, anchor, status
└── LinkAudit.tsx              # Audit history with snapshots

11.6 Backend: Celery Tasks for Linking

# sag/tasks.py — add:

@shared_task
def generate_links_for_published_content(content_id):
    """After content publishes, generate/update SAG links."""
    pass

@shared_task  
def run_link_audit(blueprint_id):
    """Weekly link audit for a blueprint."""
    pass

@shared_task
def check_broken_links(blueprint_id):
    """Check all published SAGLinks for broken targets."""
    pass

12.1 What This Phase Delivers

Automated backlink campaign generation from SAG blueprints. Implements Doc 4 — External Backlink Campaign Specification. New models, new UI, external API integrations (FatGrid, PR distribution).

12.2 Backend: New Models

Add to sag/models.py:

class CountryMarketProfile(models.Model):
    """Country-specific campaign parameters. Admin-editable."""
    country_code = models.CharField(max_length=5, primary_key=True)
    country_name = models.CharField(max_length=100)
    competition_floor = models.CharField(max_length=20)
    profile_data = models.JSONField(help_text='Full country profile: DR targets, budgets, link mix, anchor mix, velocity, quality thresholds, KPI milestones')
    is_active = models.BooleanField(default=True)
    updated_at = models.DateTimeField(auto_now=True)

class SAGCampaign(AccountBaseModel):
    """A backlink campaign generated from a SAG blueprint for a specific market."""
    id = models.UUIDField(primary_key=True, default=uuid4)
    blueprint = models.ForeignKey(SAGBlueprint, on_delete=models.CASCADE, related_name='campaigns')
    site = models.ForeignKey('auth.Site', on_delete=models.CASCADE)
    country_code = models.CharField(max_length=5)
    status = models.CharField(max_length=20, choices=[
        ('draft', 'Draft'), ('active', 'Active'),
        ('paused', 'Paused'), ('completed', 'Completed'),
    ], default='draft')
    tier_assignments = models.JSONField(default=dict, help_text='{content_id: tier}')
    total_links_target = models.IntegerField(default=0)
    total_budget_estimate = models.JSONField(default=dict, help_text='{min: X, max: Y}')
    timeline_months = models.IntegerField(default=0)
    monthly_plan = models.JSONField(default=list)
    anchor_text_plan = models.JSONField(default=dict)
    current_month = models.IntegerField(default=0)
    kpi_data = models.JSONField(default=dict)
    started_at = models.DateTimeField(null=True, blank=True)
    completed_at = models.DateTimeField(null=True, blank=True)

class SAGBacklink(AccountBaseModel):
    """Individual backlink tracked within a campaign."""
    id = models.UUIDField(primary_key=True, default=uuid4)
    blueprint = models.ForeignKey(SAGBlueprint, on_delete=models.CASCADE)
    campaign = models.ForeignKey(SAGCampaign, on_delete=models.CASCADE, related_name='backlinks')
    target_content = models.ForeignKey('writer.Content', on_delete=models.CASCADE, null=True, blank=True)
    target_url = models.URLField()
    target_tier = models.CharField(max_length=5)
    source_url = models.URLField(blank=True)
    source_domain = models.CharField(max_length=200, blank=True)
    source_dr = models.IntegerField(null=True, blank=True)
    source_traffic = models.IntegerField(null=True, blank=True)
    anchor_text = models.CharField(max_length=300, blank=True)
    anchor_type = models.CharField(max_length=30, blank=True)
    link_type = models.CharField(max_length=30, blank=True)
    marketplace = models.CharField(max_length=100, blank=True)
    cost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    quality_score = models.FloatField(default=0.0)
    country_relevant = models.BooleanField(default=False)
    date_built = models.DateField(null=True, blank=True)
    date_live = models.DateField(null=True, blank=True)
    date_last_checked = models.DateField(null=True, blank=True)
    status = models.CharField(max_length=20, choices=[
        ('ordered', 'Ordered'), ('pending', 'Pending'), ('live', 'Live'),
        ('dead', 'Dead'), ('removed', 'Removed'), ('replaced', 'Replaced'),
    ], default='ordered')
    notes = models.TextField(blank=True)

class SAGCampaignSnapshot(models.Model):
    """Monthly KPI snapshot for a campaign."""
    id = models.UUIDField(primary_key=True, default=uuid4)
    campaign = models.ForeignKey(SAGCampaign, on_delete=models.CASCADE, related_name='snapshots')
    snapshot_date = models.DateField()
    kpi_values = models.JSONField(help_text='All 18+ KPI metrics for this month')
    comparison_to_previous = models.JSONField(default=dict)

12.3 Backend: Campaign Services

sag/services/campaign_service.py    # NEW

Functions:

  • generate_campaign(blueprint, country_code, budget_override=None) → creates SAGCampaign with full plan
  • assign_tiers(blueprint, clusters) → auto-classify pages into T1-T5
  • generate_monthly_plan(campaign) → month-by-month execution plan
  • generate_anchor_text_plan(campaign) → pre-generated anchors per target page
  • calculate_budget(campaign) → budget estimate from link counts × cost ranges
  • detect_tipping_point(campaign) → check if authority tipping point indicators are met

12.4 Backend: External API Integrations

sag/integrations/                    # NEW
├── __init__.py
├── fatgrid.py                       # FatGrid API client (publisher discovery, pricing)
├── pr_distribution.py               # PRNews.io / EIN Presswire / Linking News
└── ahrefs.py                        # Ahrefs API (DR/DA tracking, lost links)

These integrations are optional — campaigns can be generated and tracked without them. The APIs enrich the campaign with real marketplace data and automated monitoring.

12.5 Frontend: Campaign UI

frontend/src/pages/
└── Campaigns/                       # NEW page (or sub-page of Linker)
    ├── CampaignOverview.tsx         # Campaign dashboard: progress, budget, current month
    ├── CampaignGenerator.tsx        # Generate new campaign wizard
    ├── MonthlyPlan.tsx              # This month's targets vs actual
    ├── LinkTracker.tsx              # Filterable table of all backlinks
    ├── TierBreakdown.tsx            # T1-T5 progress visualization
    ├── KPIDashboard.tsx             # Monthly trend charts (DR, traffic, keywords)
    ├── TippingPointDetector.tsx     # Authority tipping point indicators
    └── LinkOpportunities.tsx        # FatGrid publisher search (if integrated)

Sidebar addition:

WORKFLOW
  ├── Planner
  ├── Writer
  ├── Automation
  ├── Blueprint
  ├── Linker (internal links)
  ├── Campaigns (NEW — external backlink campaigns, if campaign_enabled)
  └── Optimizer

Feature flag: campaign_enabled — follows same pattern as linker_enabled via GlobalModuleSettings.


13. Frontend Navigation — Updated Sidebar

After all SAG phases are complete, the sidebar becomes:

Dashboard (with SAG health + blueprint progress widgets)
SETUP
  ├── Add Keywords (now shows "optional" when blueprint active)
  ├── Content Settings
  ├── Sites (setup wizard now includes Step 3 Site Builder)
  └── Thinker (admin only)
WORKFLOW
  ├── Planner (Keywords → Clusters → Ideas — enhanced with blueprint source)
  ├── Writer (Queue → Drafts → Images → Review → Published — shows cluster context)
  ├── Automation (7→8 stages when linker active, SAG mode indicator)
  ├── Blueprint (NEW — health, clusters, attributes, execution plan)
  ├── Linker (SAG-aware internal linking — when linker_enabled)
  ├── Campaigns (External backlink campaigns — when campaign_enabled)
  └── Optimizer (when optimizer_enabled)
ACCOUNT
  ├── Account Settings
  ├── Plans & Billing
  ├── Usage
  └── AI Models (admin only)
ADMIN (admin only)
  └── Sector Templates (manage attribute templates)
HELP
  └── Help & Docs

14. API Endpoint Registry

Complete list of new API endpoints introduced across all phases:

SAG Core (Phase 1-3)

GET    /api/v1/sag/blueprints/                    # List blueprints for current site
POST   /api/v1/sag/blueprints/                    # Create blueprint (admin/internal)
GET    /api/v1/sag/blueprints/{id}/               # Blueprint detail with attributes + clusters
POST   /api/v1/sag/blueprints/{id}/confirm/       # Draft → Active
POST   /api/v1/sag/blueprints/{id}/archive/       # Active → Archived
POST   /api/v1/sag/blueprints/{id}/regenerate/    # Create new version
GET    /api/v1/sag/blueprints/{id}/health_check/  # Run health check
GET    /api/v1/sag/attributes/                     # List attributes for current blueprint
GET    /api/v1/sag/clusters/                       # List clusters for current blueprint
GET    /api/v1/sag/clusters/{id}/                  # Cluster detail with keywords, content, links
GET    /api/v1/sag/templates/                      # Admin: list sector templates
POST   /api/v1/sag/templates/                      # Admin: create template
PUT    /api/v1/sag/templates/{id}/                 # Admin: update template

Site Builder Wizard (Phase 4)

POST   /api/v1/sag/wizard/generate-attributes/     # Industry+Sectors → attribute framework
POST   /api/v1/sag/wizard/populate-attributes/      # Attributes+BusinessData → populated attributes
POST   /api/v1/sag/wizard/generate-blueprint/       # Populated attributes → full blueprint
POST   /api/v1/sag/wizard/confirm-blueprint/        # Blueprint → active, trigger taxonomy creation

Existing Site Analysis (Phase 7)

POST   /api/v1/sag/analysis/extract-attributes/    # Site data → discovered attributes
POST   /api/v1/sag/analysis/generate-gap-report/   # Blueprint + site data → gap report
POST   /api/v1/sag/analysis/auto-tag-products/     # AI product attribute assignment

Interlinking (Phase 9)

GET    /api/v1/sag/links/                          # List all SAG links for blueprint
POST   /api/v1/sag/links/generate/                 # Generate links for content
GET    /api/v1/sag/links/audit/                    # Run link audit
GET    /api/v1/sag/links/audit/history/            # Audit snapshots
GET    /api/v1/sag/links/missing/                  # Missing mandatory links
GET    /api/v1/sag/campaigns/                      # List campaigns
POST   /api/v1/sag/campaigns/generate/             # Generate campaign from blueprint + country
GET    /api/v1/sag/campaigns/{id}/                 # Campaign detail
PATCH  /api/v1/sag/campaigns/{id}/                 # Update status, adjust plan
GET    /api/v1/sag/campaigns/{id}/monthly/         # Current month plan
GET    /api/v1/sag/campaigns/{id}/kpi/             # KPI dashboard data
GET    /api/v1/sag/campaigns/{id}/tipping-point/   # Tipping point indicators
GET    /api/v1/sag/backlinks/                      # List all backlinks for campaign
POST   /api/v1/sag/backlinks/                      # Log new backlink
PATCH  /api/v1/sag/backlinks/{id}/                 # Update backlink status
GET    /api/v1/sag/countries/                      # List country market profiles
GET    /api/v1/sag/fatgrid/search/                 # FatGrid publisher search (proxy)

15. AI Function Registry

New AI functions to register in ai/registry.py:

Function Key Location Input Output
discover_sector_attributes sag/ai_functions/attribute_discovery.py Industry, sector, site_type Attribute framework JSON
extract_site_attributes sag/ai_functions/attribute_extraction.py Raw site data Discovered attributes with confidence
populate_attributes sag/ai_functions/attribute_population.py Attributes + business data Populated attribute values
form_clusters sag/ai_functions/cluster_formation.py Populated attributes, sector Clusters with types
generate_keywords sag/ai_functions/keyword_generation.py Clusters + templates Per-cluster keyword arrays
plan_content sag/ai_functions/content_planning.py Clusters + keywords + existing content Content ideas with Structure + Type
generate_anchor_text sag/ai_functions/anchor_text.py Source, target, link_type, cluster Anchor text variants

All functions use the existing AIEngine → Function → Provider pattern. They're registered the same way as AutoClusterKeywords and GenerateContentIdeas.


16. Celery Task Registry

New periodic and on-demand tasks:

Task Schedule Trigger Location
run_blueprint_health_check Weekly (Celery Beat) Also manual from dashboard sag/tasks.py
check_blueprint_evolution_triggers Weekly (Celery Beat) sag/tasks.py
generate_links_for_published_content On demand After content publishes sag/tasks.py
run_link_audit Weekly (Celery Beat) Also manual from Linker sag/tasks.py
check_broken_links Weekly (Celery Beat) sag/tasks.py
check_backlink_status Weekly (Celery Beat) sag/tasks.py
generate_campaign_snapshot Monthly (Celery Beat) sag/tasks.py
detect_tipping_point Monthly (Celery Beat) After snapshot sag/tasks.py

17. Feature Flag Registry

Flag Controls Default Phase
sag_enabled Blueprint generation, wizard Step 3, blueprint-aware pipeline False Phase 1
linker_enabled Linker module, SAG link generation, link audit False (exists) Phase 9
campaign_enabled External backlink campaigns, FatGrid integration False Phase 10

All flags follow existing pattern: GlobalModuleSettings (platform-wide) + ModuleEnableSettings (per-account). Checked in sidebar rendering and API permission classes.

When sag_enabled is False:

  • Setup wizard skips Step 3 (current behavior preserved)
  • Pipeline runs in legacy mode (current behavior preserved)
  • Blueprint page hidden from sidebar
  • All SAG API endpoints return 403

18. Cross-Reference: What Feeds What

SectorAttributeTemplate
  └── feeds → Blueprint generation (Layer 1 attributes)
  
SAGBlueprint
  ├── feeds → Content pipeline (idea generation, task context, type-specific prompts)
  ├── feeds → Taxonomy creation (plugin creates WP taxonomies)
  ├── feeds → Linker module (link rules derived from cluster structure)
  ├── feeds → Campaign module (tier assignments from cluster types + keyword volumes)
  └── feeds → Health monitoring (completeness tracking)

SAGCluster
  ├── feeds → Content Ideas (hub + supporting content per cluster)
  ├── feeds → Keywords (auto-generated from attribute intersections)
  ├── feeds → Internal links (hub↔supporting, cross-cluster, taxonomy contextual)
  └── feeds → Backlink tiers (T1-T5 assignment based on cluster type + volume)

SAGLink
  ├── feeds → Content HTML (links injected before publish)
  ├── feeds → Link audit (health scores, gap analysis)
  └── feeds → Blueprint health score (link coverage component)

SAGCampaign
  ├── feeds → KPI dashboard (monthly snapshots, trend charts)
  ├── feeds → Tipping point detection (compares metrics vs country thresholds)
  └── feeds → Blueprint health score (backlink coverage component)

GSC Data (from Consolidated Module Plans — separate implementation)
  ├── feeds → Campaign KPI dashboard (organic traffic, keywords, impressions)
  ├── feeds → Tipping point detection (pages ranking without backlinks)
  └── feeds → Blueprint health (indexed pages, crawl status)

Reference Documents

This guide draws from and should be used alongside:

Document Purpose Location
SAG-Master-Document.md Pure SAG methodology — clustering rules, dimensional philosophy Project knowledge
SAG-IGNY8-Implementation.md Three-layer architecture, two entry scenarios, data models, pipeline integration Project knowledge
SAG-Doc3-Interlinking-Specification-PLAN.md Internal linking rules, 7 link types, authority flow, scoring Project knowledge
SAG-Doc4-External-Backlink-Campaign-PLAN.md Campaign generation, country profiles, FatGrid/PR integration, KPI tracking Project knowledge
IGNY8-Current-State.md Complete current platform state — all file paths, models, modules, navigation Project knowledge
SAG-Niche-Definition-Process.docx Cluster formation rules, max 50 clusters, hub + supporting structure Project knowledge
IGNY8-Industry-Sector-Master-List.md All industries and sectors in IGNY8 Project knowledge
SAGIndustry01HealthcareMedical.xlsx Reference template format for sector attribute frameworks Project knowledge

End of Doc A — SAG Architecture Development Guide