Files
igny8/v2/V2-Execution-Docs/02A-content-types-extension.md
IGNY8 VPS (Salman) 0570052fec 1
2026-03-23 17:20:51 +00:00

25 KiB
Raw Blame History

IGNY8 Phase 2: Content Types Extension (02A)

Type-Specific Content Generation Pipeline

Document Version: 1.0 Date: 2026-03-23 Phase: IGNY8 Phase 2 — Feature Expansion Status: Build Ready Source of Truth: Codebase at /data/app/igny8/ Audience: Claude Code, Backend Developers, Architects


1. CURRENT STATE

Content Model Today

The Content model (app_label=writer, db_table=igny8_content) already supports multiple content types via two fields:

CONTENT_TYPE_CHOICES = [
    ('post', 'Post'),
    ('page', 'Page'),
    ('product', 'Product'),
    ('taxonomy', 'Taxonomy'),
]

CONTENT_STRUCTURE_CHOICES = [
    ('article', 'Article'),
    ('guide', 'Guide'),
    ('comparison', 'Comparison'),
    ('review', 'Review'),
    ('listicle', 'Listicle'),
    ('landing_page', 'Landing Page'),
    ('business_page', 'Business Page'),
    ('service_page', 'Service Page'),
    ('general', 'General'),
    ('cluster_hub', 'Cluster Hub'),
    ('product_page', 'Product Page'),
    ('category_archive', 'Category Archive'),
    ('tag_archive', 'Tag Archive'),
    ('attribute_archive', 'Attribute Archive'),
]

These same choices exist on Tasks (db_table=igny8_tasks) and ContentIdeas (db_table=igny8_content_ideas).

What Works Now

  • The generate_content AI function in igny8_core/ai/functions/generate_content.py produces blog-style articles regardless of the content_type field
  • Only content_type='post' with content_structure='article' is actively used by the automation pipeline
  • Pipeline Stage 4 (Tasks → Content) does not route to type-specific prompts
  • No type-specific section layouts, presets, or schema generation exist

Phase 1 Foundation Available

  • SAGCluster.cluster_type choices: product_category, condition_problem, feature, brand, informational, comparison
  • SAGCluster.hub_page_type (default cluster_hub) and hub_page_structure (guide_tutorial, product_comparison, category_overview, problem_solution, resource_library)
  • 01E blueprint-aware pipeline provides blueprint_context to each stage with cluster_type, content_structure, and content_type fields
  • 01E defines 12 content type → template key mappings (e.g., sag_hub_guide, sag_blog_comparison, sag_product_page)

Gap

The template keys from 01E (sag_hub_guide, sag_blog_comparison, etc.) route to LLM prompt templates — but those templates don't exist yet. The actual type-specific prompt logic, section layouts, field schemas, and generation presets are what this doc delivers.


2. WHAT TO BUILD

Overview

Extend the content generation pipeline to produce structurally different output for 6 content type categories. Each type gets:

  • Section layout templates — defining the structure of the generated content (sections, order, constraints)
  • Type-specific AI prompts — prompt templates tailored to the content type's purpose
  • Generation presets — default word counts, image counts, schema types, tone
  • Structured data fields — type-specific data (product specs, service steps, comparison items) stored in a JSONField

Content Type Extensions

Type 1: Pages (content_type=page)

Structure Purpose Section Layout
landing_page Conversion-focused landing page Hero → Features → Benefits → Social Proof → CTA
business_page About/company page Company Intro → History → Values → Team → CTA
service_page Service offering page Problem → Solution → Process → Outcomes → Pricing → FAQ → CTA
general Generic page Intro → Body Sections → CTA
cluster_hub Cluster hub/pillar page Overview → Subtopic Grid → Detailed Guides → Internal Links → FAQ
  • AI prompt tone: Professional, conversion-focused, concise, benefit-driven
  • Schema: WebPage (default), AboutPage, ContactPage as appropriate
  • Default word count: 1,0003,000 depending on structure

Type 2: Products (content_type=product)

Structure Purpose Section Layout
product_page Single product review/description Overview → Features → Specifications → Pros/Cons → Verdict
comparison A vs B product comparison Introduction → Feature Matrix → Category Breakdown → Verdict
listicle Top products roundup Introduction → Product Cards (ranked) → Comparison Table → Verdict
  • Structured data fields: price_range (JSON), features (JSON array), specifications (JSON), pros_cons (JSON {pros: [], cons: []})
  • AI prompt tone: Feature-benefit mapping, objective analysis, buyer-persona aware
  • Schema: Product with offers, aggregateRating, review
  • Image presets: Product hero image, feature highlight visuals, comparison table graphic
  • Default word count: 1,5004,000

Type 3: Services (content_type=page, content_structure=service_page)

Structure Purpose Section Layout
service_page Core service offering page Problem → Solution → Process Steps → Outcomes → Pricing Tiers → FAQ → CTA
landing_page Service-specific landing (area variant) Hero → Service Intro → Benefits → Testimonials → Area Info → CTA
  • Structured data fields: process_steps (JSON array), outcomes (JSON array), pricing_tiers (JSON), areas_served (JSON array), faqs (JSON array of {question, answer})
  • AI prompt tone: Problem-solution, trust-building, process explanation, CTA-heavy
  • Schema: Service, ProfessionalService, LocalBusiness+hasOfferCatalog
  • Geographic targeting: Generate area-specific variations from a base service page
  • Default word count: 1,5003,500

Type 4: Company Pages (content_type=page, content_structure=business_page)

Structure Purpose
business_page About company, team, careers, press pages
  • AI prompt tone: Brand voice emphasis, story-driven, credibility markers
  • Schema: Organization, AboutPage
  • Default word count: 8002,000

Type 5: Comparison Pages (content_type=post, content_structure=comparison)

Structure Purpose Section Layout
comparison A vs B analysis Introduction → Feature Matrix → Category-by-Category → Winner → FAQ
listicle Top N alternatives/roundup Introduction → Comparison Table → Individual Reviews → Verdict
  • Structured data fields: comparison_items (JSON array of {name, features, pros, cons, rating, verdict})
  • AI prompt tone: Objective analysis, data-driven, comparison tables, winner selection with reasoning
  • Schema: Article with itemListElement (for multi-comparison)
  • Default word count: 2,0005,000

Type 6: Brand Pages (content_type=page, content_structure=brand_page)

Structure Purpose
brand_page Brand overview, review, or alternative recommendation page
  • AI prompt tone: Brand-focused, factual, company background included
  • Schema: Organization (for overview), Article (for review)
  • Default word count: 1,0003,000

Blueprint-to-Type Mapping

When the pipeline executes with SAG context (01E), the SAGCluster.cluster_type informs which content types to generate:

SAGCluster.cluster_type Primary content_type Primary content_structure
informational post article, guide
comparison post comparison, listicle
product_category product product_page, listicle
feature page landing_page, service_page
brand page brand_page
condition_problem post guide, article

Hub pages for any cluster type use content_type=page, content_structure=cluster_hub.


3. DATA MODELS & APIs

New Choices (add to existing choice lists)

# Add to CONTENT_STRUCTURE_CHOICES on Content, Tasks, ContentIdeas
('brand_page', 'Brand Page'),

Note: Most structures already exist in the codebase. Only brand_page is new.

Modified Models

Content (db_table=igny8_content) — add fields:

sections = models.JSONField(
    default=list, blank=True,
    help_text="Ordered section data for structured content types"
)
# Structure: [{"type": "hero", "heading": "...", "body": "...", "cta": "..."}, ...]

structured_data = models.JSONField(
    default=dict, blank=True,
    help_text="Type-specific data: product specs, service steps, comparison items"
)
# Structure varies by content_type — see type definitions above

Tasks (db_table=igny8_tasks) — add fields:

structure_template = models.JSONField(
    default=dict, blank=True,
    help_text="Section layout template for content generation"
)
# Structure: {"sections": [{"type": "hero", "required": true, "max_words": 200}, ...]}

type_presets = models.JSONField(
    default=dict, blank=True,
    help_text="Type-specific generation parameters"
)
# Structure: {"tone": "professional", "schema_type": "Product", "image_count": 3, ...}

New Model

class ContentTypeTemplate(AccountBaseModel):
    """
    Defines section layout and AI prompt templates per content_type + content_structure combination.
    System-provided defaults (is_system=True) plus per-account custom templates.
    """
    content_type = models.CharField(max_length=50, choices=CONTENT_TYPE_CHOICES, db_index=True)
    content_structure = models.CharField(max_length=50, choices=CONTENT_STRUCTURE_CHOICES, db_index=True)
    template_name = models.CharField(max_length=200)
    section_layout = models.JSONField(
        default=list,
        help_text="Ordered sections: [{type, label, required, max_words, guidance}]"
    )
    ai_prompt_template = models.TextField(
        help_text="Base AI prompt template for this type. Supports {variables}."
    )
    default_schema_type = models.CharField(max_length=100, blank=True, default='')
    default_word_count_min = models.IntegerField(default=1000)
    default_word_count_max = models.IntegerField(default=3000)
    default_image_count = models.IntegerField(default=2)
    tone = models.CharField(max_length=100, default='professional')
    is_system = models.BooleanField(default=False, help_text="System-provided template (not editable by users)")
    is_active = models.BooleanField(default=True)

    class Meta:
        app_label = 'writer'
        db_table = 'igny8_content_type_templates'
        unique_together = [['account', 'content_type', 'content_structure', 'template_name']]
        ordering = ['content_type', 'content_structure']

Migration

igny8_core/migrations/XXXX_content_types_extension.py

Fields added:

  1. Content.sections — JSONField, default=list
  2. Content.structured_data — JSONField, default=dict
  3. Tasks.structure_template — JSONField, default=dict
  4. Tasks.type_presets — JSONField, default=dict
  5. ContentTypeTemplate new table
  6. Add ('brand_page', 'Brand Page') to CONTENT_STRUCTURE_CHOICES

API Endpoints

Content Type Templates:

GET    /api/v1/writer/content-type-templates/                        # List templates (filtered by content_type, content_structure)
POST   /api/v1/writer/content-type-templates/                        # Create custom template
GET    /api/v1/writer/content-type-templates/{id}/                   # Template detail
PUT    /api/v1/writer/content-type-templates/{id}/                   # Update custom template
DELETE /api/v1/writer/content-type-templates/{id}/                   # Delete custom template (system templates cannot be deleted)
GET    /api/v1/writer/content-type-templates/{id}/preview/           # Preview: generate sample section layout

Modified Endpoints:

POST   /api/v1/writer/tasks/                                         # Extend: accepts structure_template + type_presets
POST   /api/v1/writer/generate/                                      # Extend: routes to type-specific AI prompt
GET    /api/v1/writer/content/{id}/                                  # Response now includes sections + structured_data

ViewSet:

# igny8_core/modules/writer/views/content_type_template_views.py
class ContentTypeTemplateViewSet(AccountModelViewSet):
    serializer_class = ContentTypeTemplateSerializer
    queryset = ContentTypeTemplate.objects.all()
    filterset_fields = ['content_type', 'content_structure', 'is_system', 'is_active']

    def get_queryset(self):
        # Return system templates + account's custom templates
        return ContentTypeTemplate.objects.filter(
            models.Q(is_system=True) | models.Q(account=self.request.account)
        )

    @action(detail=True, methods=['get'])
    def preview(self, request, pk=None):
        template = self.get_object()
        # Return rendered section layout with placeholder content
        return Response({'sections': template.section_layout})

URL Registration:

# igny8_core/modules/writer/urls.py — add to existing router
router.register('content-type-templates', ContentTypeTemplateViewSet, basename='content-type-template')

AI Function Extension

Extend GenerateContentFunction in igny8_core/ai/functions/generate_content.py:

class GenerateContentFunction(BaseAIFunction):
    def prepare(self, payload: dict, account=None) -> Any:
        tasks = super().prepare(payload, account)
        for task in tasks:
            # Load template if not already set on task
            if not task.structure_template:
                template = ContentTypeTemplate.objects.filter(
                    models.Q(account=account) | models.Q(is_system=True),
                    content_type=task.content_type,
                    content_structure=task.content_structure,
                    is_active=True
                ).order_by('-is_system').first()  # Prefer account-specific over system
                if template:
                    task._template = template
        return tasks

    def build_prompt(self, data: Any, account=None) -> str:
        task = data  # Single task (batch_size=1)
        template = getattr(task, '_template', None)

        if template:
            prompt = template.ai_prompt_template.format(
                title=task.title,
                keywords=task.keywords or '',
                word_count=task.word_count,
                content_type=task.content_type,
                content_structure=task.content_structure,
                sections=json.dumps(template.section_layout),
                schema_type=template.default_schema_type,
                tone=template.tone,
            )
        else:
            # Fallback to existing blog-style prompt
            prompt = self._build_default_prompt(task)

        # Inject blueprint context if available (from 01E)
        blueprint_context = getattr(task, 'blueprint_context', None)
        if blueprint_context:
            prompt += f"\n\nCluster Context: {json.dumps(blueprint_context)}"

        return prompt

    def parse_response(self, response: str, step_tracker=None) -> Any:
        parsed = super().parse_response(response, step_tracker)
        # Extract sections array and structured_data if present in AI response
        if isinstance(parsed, dict):
            parsed.setdefault('sections', [])
            parsed.setdefault('structured_data', {})
        return parsed

    def save_output(self, parsed, original_data, account=None, **kwargs) -> Dict:
        result = super().save_output(parsed, original_data, account, **kwargs)
        # Persist sections and structured_data on Content
        if 'content_id' in result:
            Content.objects.filter(id=result['content_id']).update(
                sections=parsed.get('sections', []),
                structured_data=parsed.get('structured_data', {}),
            )
        return result

System Template Seed Data

Create a management command to seed default templates:

# igny8_core/management/commands/seed_content_type_templates.py

Seed templates (is_system=True, account=None):

content_type content_structure template_name default_schema_type word_count_range
post article Blog Article Article 10002500
post guide Comprehensive Guide Article 20004000
post comparison Comparison Article Article 20005000
post review Product Review Review 15003000
post listicle Listicle Article 15003500
page landing_page Landing Page WebPage 10002500
page business_page Business Page AboutPage 8002000
page service_page Service Page Service 15003500
page general General Page WebPage 5002000
page cluster_hub Cluster Hub Page CollectionPage 20005000
page brand_page Brand Page Organization 10003000
product product_page Product Page Product 15004000
taxonomy category_archive Category Archive CollectionPage 5001500
taxonomy tag_archive Tag Archive CollectionPage 5001500
taxonomy attribute_archive Attribute Archive CollectionPage 5001500

Credit Costs

No change to existing credit costs. Type routing changes the prompt structure but not the token volume — still 48 credits per content generation via CreditCostConfig(operation_type='content_generation').


4. IMPLEMENTATION STEPS

Step 1: Add New Fields to Existing Models

# Add fields to Content, Tasks models
# Add 'brand_page' to CONTENT_STRUCTURE_CHOICES

Files to modify:

  • backend/igny8_core/business/content/models.py — add sections, structured_data to Content; add structure_template, type_presets to Tasks; add brand_page to CONTENT_STRUCTURE_CHOICES
  • backend/igny8_core/business/planning/models.py — add brand_page to ContentIdeas CONTENT_STRUCTURE_CHOICES

Step 2: Create ContentTypeTemplate Model

# Create new model in writer app

File to create:

  • backend/igny8_core/business/content/content_type_template.py (or add to existing models.py)

Step 3: Create and Run Migration

cd /data/app/igny8/backend
python manage.py makemigrations --name content_types_extension
python manage.py migrate

Step 4: Create Serializers

Files to create/modify:

  • backend/igny8_core/modules/writer/serializers/content_type_template_serializer.py
  • Modify existing content serializer to include sections and structured_data
  • Modify existing task serializer to include structure_template and type_presets

Step 5: Create ViewSet and URLs

Files to create:

  • backend/igny8_core/modules/writer/views/content_type_template_views.py
  • Modify backend/igny8_core/modules/writer/urls.py — register new ViewSet

Step 6: Extend GenerateContentFunction

File to modify:

  • backend/igny8_core/ai/functions/generate_content.py — add type routing logic

Step 7: Create System Template Seed Command

File to create:

  • backend/igny8_core/management/commands/seed_content_type_templates.py
python manage.py seed_content_type_templates

Step 8: Create Type-Specific Prompt Templates

Files to create in backend/igny8_core/ai/prompts/:

  • page_prompts.py — landing_page, business_page, service_page, general, cluster_hub
  • product_prompts.py — product_page, product comparison, product roundup
  • comparison_prompts.py — versus, multi-comparison, alternatives
  • brand_prompts.py — brand overview, brand review

Step 9: Frontend Updates

Files to create/modify in frontend/src/:

  • pages/Writer/ContentTypeTemplates.tsx — template management page
  • stores/contentTypeTemplateStore.ts — Zustand store
  • api/contentTypeTemplates.ts — API client
  • Modify task creation form to show type-specific template selection
  • Modify content viewer to render section-based content

Step 10: Tests

cd /data/app/igny8/backend
python manage.py test igny8_core.business.content.tests.test_content_type_templates
python manage.py test igny8_core.ai.tests.test_generate_content_types

5. ACCEPTANCE CRITERIA

  • Content.sections and Content.structured_data fields exist and migrate successfully
  • Tasks.structure_template and Tasks.type_presets fields exist and migrate successfully
  • ContentTypeTemplate model created with all fields, igny8_content_type_templates table exists
  • brand_page added to CONTENT_STRUCTURE_CHOICES on Content, Tasks, ContentIdeas
  • 15 system templates seeded via management command
  • GET /api/v1/writer/content-type-templates/ returns system + account templates
  • POST /api/v1/writer/content-type-templates/ creates custom template (is_system=False)
  • System templates cannot be modified or deleted by account users
  • GenerateContentFunction routes to type-specific prompt when template exists
  • GenerateContentFunction falls back to existing blog-style prompt when no template found
  • Content generated with type template populates sections and structured_data fields
  • Blueprint context from 01E is injected into prompts when SAG data available
  • Frontend template management page allows CRUD on custom templates
  • Task creation form shows template selection filtered by content_type + content_structure
  • All new API endpoints require authentication and enforce account isolation
  • Existing content_type='post' + content_structure='article' generation works unchanged (backward compatible)

6. CLAUDE CODE INSTRUCTIONS

Execution Order

  1. Read backend/igny8_core/business/content/models.py — understand existing Content and Tasks models
  2. Read backend/igny8_core/ai/functions/generate_content.py — understand current generation logic
  3. Read backend/igny8_core/ai/base.py and backend/igny8_core/ai/registry.py — understand base pattern
  4. Add new fields + ContentTypeTemplate model
  5. Create migration, run makemigrations + migrate
  6. Build serializers, ViewSet, URLs
  7. Extend GenerateContentFunction with type routing
  8. Create seed command and run it
  9. Build prompt templates per type
  10. Build frontend components

Key Constraints

  • ALL primary keys are BigAutoField (integer). No UUIDs anywhere.
  • Model class names are PLURAL where applicable: Tasks, ContentIdeas, Clusters, Keywords, Images. Content stays singular.
  • Frontend files use .tsx extension, Zustand for state management, Vitest for testing
  • Celery app name is igny8_core
  • All new tables use igny8_ prefix
  • Follow existing ViewSet pattern: AccountModelViewSet for account-scoped resources
  • Follow existing serializer pattern: ModelSerializer with explicit fields
  • Follow existing URL pattern: register on DefaultRouter in igny8_core/modules/writer/urls.py

File Tree (New/Modified)

backend/igny8_core/
├── business/content/
│   └── models.py                                    # MODIFY: add sections, structured_data, structure_template, type_presets, brand_page choice
│   └── content_type_template.py                     # NEW: ContentTypeTemplate model (or add to models.py)
├── business/planning/
│   └── models.py                                    # MODIFY: add brand_page to ContentIdeas CONTENT_STRUCTURE_CHOICES
├── ai/functions/
│   └── generate_content.py                          # MODIFY: type routing + template-aware prompt building
├── ai/prompts/
│   ├── page_prompts.py                              # NEW: landing page, business page, service page prompts
│   ├── product_prompts.py                           # NEW: product page, comparison, roundup prompts
│   ├── comparison_prompts.py                        # NEW: versus, multi-comparison prompts
│   └── brand_prompts.py                             # NEW: brand overview/review prompts
├── management/commands/
│   └── seed_content_type_templates.py               # NEW: seed system templates
├── modules/writer/
│   ├── serializers/
│   │   └── content_type_template_serializer.py      # NEW
│   ├── views/
│   │   └── content_type_template_views.py           # NEW
│   └── urls.py                                      # MODIFY: register new ViewSet
├── migrations/
│   └── XXXX_content_types_extension.py              # NEW: auto-generated

frontend/src/
├── pages/Writer/
│   └── ContentTypeTemplates.tsx                     # NEW: template management
├── stores/
│   └── contentTypeTemplateStore.ts                  # NEW: Zustand store
├── api/
│   └── contentTypeTemplates.ts                      # NEW: API client

Cross-References

  • 01E (blueprint-aware pipeline): blueprint_context injection, cluster_type → content_type mapping
  • 01A (SAG data foundation): SAGCluster.cluster_type, hub_page_type, hub_page_structure
  • 02B (taxonomy term content): uses content_type=taxonomy with ContentTypeTemplate
  • 02G (rich schema): schema_type from ContentTypeTemplate.default_schema_type
  • 03B (WP plugin connected): content sync maps content_type to WordPress post types