0. GOAL When this refactor is complete: You enter keywords in Planner. Clusters and ideas are created. Writer generates tasks and content. Content Manager becomes the single source of truth. One click publish sends content to WordPress. All mappings (cluster, taxonomies, site) are correct. Linker, Optimizer, IGNY8 hosted sites stay out of scope for now. This spec assumes all earlier decisions you made about models and statuses are final. 1. END TO END FLOW (KEYWORD TO PUBLISH) 1.1 High level flow Planner Input keywords Generate clusters Generate ideas per cluster Convert ideas into Writer tasks Writer Tasks created with content_type and content_structure AI generates draft content Task marked completed Content saved as draft entry Content Manager Lists all content for a site (imported and AI written) You edit, assign cluster and taxonomies if needed You click publish for selected content Publish to WordPress IGNY8 sends content HTML and metadata to WP via REST WP creates or updates post / page / product WP returns ID and URL IGNY8 saves external_id and external_url Content status goes from draft to published Task status already completed when content was generated Only Content has draft/published. Only Task has queued/completed. Planner and Writer do not track sync. 2. BACKEND DATA MODEL (FINAL) This is the corrected model set the whole system is built on. 2.1 Cluster Pure topic. Parent of everything else. Not tied to a content type. Fields: id site sector name description created_at updated_at Remove: context_type, dimension_meta. 2.2 Task Planner → Writer contract for content creation. Fields: id site sector cluster (FK → Cluster, required) content_type (Article, Page, Product, Taxonomy) content_structure (Review, Comparison, Tutorial, Listicle, Landing Page, Archive Page, etc) taxonomy_term (FK → ContentTaxonomy, optional, used when targeting a taxonomy archive) keywords (M2M → Keyword) status (queued, completed) created_at updated_at Remove: cluster_role, sync_status. 2.3 Content Writer output and imported WP content, managed in Content Manager. Fields: id site sector cluster (FK → Cluster, required) content_type (Article, Page, Product, Taxonomy) content_structure title content_html taxonomy_terms (M2M → ContentTaxonomy) external_id (WP post_id, nullable) external_url (WP permalink, nullable) status (draft, published) source (igny8, wordpress) created_at updated_at Remove: cluster_role, sync_status. 2.4 ContentTaxonomy WordPress taxonomies and IGNY8 cluster-like taxonomies. Fields: id site sector name slug taxonomy_type (category, tag, product_category, product_attribute, cluster, service_category if you keep it) external_taxonomy (WP taxonomy slug, e.g. category, post_tag, product_cat, pa_size; null for cluster) external_id (WP term_id; null for igy8 custom terms and clusters) created_at updated_at Remove: sync_status. 3. WORDPRESS ↔ IGNY8 DATA FLOWS 3.1 WordPress → IGNY8 (import / sync) Plugin endpoints WordPress plugin provides: /wp-json/igny8/v1/site-metadata /wp-json/igny8/v1/posts /wp-json/igny8/v1/taxonomies Possibly specific endpoints like /post-by-task-id, /post-by-content-id Plugin responsibilities: Export posts, pages, products with: post_id post_type post_title post_content permalink taxonomies (term_ids + taxonomy slugs) meta keys for any IGNY8 links (optional) Export taxonomies: term_id name slug taxonomy IGNY8 backend behaviour When importing posts: Create Content rows: site, sector inferred from site record cluster: null initially (to be assigned later) content_type mapped from WP post_type: post ⇒ Article page ⇒ Page product ⇒ Product content_structure: optional default (e.g. ImportedArticle, ImportedPage) title, content_html from WP external_id = post_id external_url = permalink status = draft source = wordpress Link taxonomy terms: For each term on the WP post: upsert ContentTaxonomy by external_id + external_taxonomy attach to Content.taxonomy_terms When importing taxonomies: For each WP term: Create ContentTaxonomy if it does not exist: name slug taxonomy_type derived from taxonomy (category, tag, product_category, product_attribute) external_taxonomy = WP taxonomy slug external_id = term_id No sync_status is needed. Imported items are identified by source=wordpress and external_id present. 3.2 IGNY8 → WordPress (publish) Publishing is done only from Content Manager, not from Planner or Writer. Backend publishing service Given a Content id: Load Content: ensure status = draft ensure site record configured with WP endpoint and auth Prepare payload: post_type based on content_type post_title from Content.title post_content from Content.content_html tax_input from Content.taxonomy_terms mapped through external_taxonomy and external_id Call WordPress REST: POST /wp-json/wp/v2/{post_type} On success: response.id ⇒ Content.external_id response.link ⇒ Content.external_url Content.status ⇒ published Do not touch Task; Task was marked completed when content was generated. If the Content row already has an external_id, publishing should update that post instead of creating a new one. WordPress plugin side Accept incoming POST from IGNY8: Validate token / key Create or update the post: set post_title, post_content, post_type, post_status=publish set taxonomies based on term IDs Return: id link Plugin may optionally store meta keys like _igny8_content_id or _igny8_cluster_id, but this is optional. 4. CONTENT MANAGER AS SOURCE OF TRUTH Content Manager is the last part of the flow. 4.1 Role Content Manager: Shows all content for a site: imported from WP generated by Writer Allows editing and assigning: cluster taxonomy_terms Controls publish to WordPress. Reflects the final status for each content item: draft published All other modules are upstream. They feed into Content Manager, but do not override its status. 4.2 Content Manager data model usage Content listing uses: Content.id Content.title Content.content_type Content.content_structure Content.cluster Content.taxonomy_terms Content.status Content.external_id Content.external_url Content.source Content.created_at / updated_at 4.3 Content Manager UX requirements Table columns: Title Type (Article / Page / Product / Taxonomy) Structure (Review, Landing Page, Archive Page, etc) Cluster (with link to cluster detail) Taxonomies (comma separated list of taxonomy term names) Status (draft/published) Source (IGNY8, WordPress) URL (clickable if published) Actions (Edit, Publish, View in WordPress) Filters: content_type status cluster taxonomy_type (optional) source Row actions: Edit: Opens editor on Content: edit title edit content_html attach or change cluster attach or change taxonomy_terms Publish: Calls backend publish service On success: status becomes published external_id and external_url are filled View in WordPress: Opens external_url in new tab 4.4 Cluster and taxonomy assignment rules New content generated from tasks: cluster is assigned automatically from the Task.cluster taxonomy_terms can be empty initially Imported content from WordPress: cluster must be manually assigned in Content Manager or via auto mapping later taxonomy_terms are imported automatically Content Manager must allow: batch cluster assignment: select multiple rows assign one cluster batch taxonomy assignment: select rows attach a taxonomy term 5. FRONTEND MODULES ALIGNED WITH THIS FLOW 5.1 Planner Key UI responsibilities: Keywords input and cluster generation Idea generation per cluster Create tasks that are passed to Writer Data it needs to pass to Writer: cluster_id content_type content_structure primary_keyword any extra parameters (tone, target country etc) 5.2 Writer Key UI responsibilities: List tasks with status (queued/completed) For each task: show cluster content_type content_structure primary_keyword Actions: Generate draft content View generated draft Writer does not publish and does not talk to WordPress directly. 5.3 Sites For this DDAY refactor, keep only: grid view of sites for each site card: Dashboard button Content Manager button Settings button Active/inactive toggle at the top Remove: Pages button and its backend logic Sectors button from card (moved into Site Settings tab) Site builder and blueprints buttons (out of scope) The Content Manager for a site is reachable from the site card. 5.4 Cluster Detail Page When clicking a cluster: Show: cluster name and description tabs or sections per content_type: Articles Pages Products Taxonomy Pages each section listing content entries linked to that cluster This view can internally reuse the Content Manager data but filtered by cluster. 6. STATE MACHINE SUMMARY 6.1 Task queued: created from Planner or manually completed: AI content generated and saved to Content table Task never changes when publish happens. 6.2 Content draft: created from Writer completion or from WordPress import published: after successful publish to WordPress There is no separate sync_status in this refactor. If external_id is present and status is published, it is live. 7. IMPLEMENTATION CHECKLIST To actually complete this DDAY refactor: Backend Update models: Remove fields: cluster_role, sync_status, context_type, dimension_meta, ContentTaxonomy.sync_status Add or rename fields: taxonomy_term on Task, taxonomy_terms M2M on Content if not already consistent Regenerate migrations. Update serializers: Remove removed fields Add or rename fields accordingly Review all viewsets and services: Task creation and listing Content creation, listing, edit WordPress import service WordPress publish service Confirm WordPress plugin endpoints match expected payloads. Frontend Planner: Ensure ideas create tasks with cluster_id, content_type, content_structure. Writer: Only show queued/completed status. No sync related fields. Link to open draft in Content Manager (optional but helpful). Sites: Remove builder-related buttons. Only show Dashboard, Content, Settings. Move toggle and sectors UI as per your earlier 18 points. Content Manager: Implement table with final columns and filters. Implement Edit and Publish actions as defined. Ensure every content row came from Content model only. Integrate cluster and taxonomy selectors. Cluster detail: Filter Content list by cluster_id and group by content_type