diff --git a/PHASE2_ROADMAP.md b/PHASE2_ROADMAP.md new file mode 100644 index 0000000..6a7a9d5 --- /dev/null +++ b/PHASE2_ROADMAP.md @@ -0,0 +1,132 @@ +# Phase 2: TDD Implementation Roadmap + +**Status:** In Progress +**Start:** 2026-05-29 +**Target:** All 64 tests passing ✅ + +## TDD Workflow (Golden Rules) + +1. **RED**: Write failing test (shows missing implementation) +2. **GREEN**: Write minimum code to make it pass +3. **REFACTOR**: Clean up while test stays green +4. **REPEAT**: Next test, next feature + +## Implementation Order (Bottom-Up) + +### Layer 1: Models (Core Data) +- [ ] `itsulu.blog.topic` — topic queue management + - [ ] `test_topic_is_created_with_pending_state` + - [ ] `test_get_next_topic_returns_highest_priority_pending_topic` + - [ ] `test_get_next_topic_returns_none_when_queue_is_empty` + - [ ] `test_mark_topic_as_used_changes_state` + - [ ] `test_topic_can_be_linked_to_a_specific_blog` + - [ ] `test_topic_name_cannot_be_empty` + - [ ] `test_used_topic_is_excluded_from_next_topic_selection` + +- [ ] `itsulu.blog.generation.log` — execution audit trail + - [ ] `test_successful_log_record_is_created_with_correct_fields` + - [ ] `test_error_log_has_no_linked_blog_post` + - [ ] `test_error_log_record_stores_human_readable_error_message` + - [ ] `test_log_trigger_source_can_be_scheduled` + - [ ] `test_log_trigger_source_records_slot_name_for_scheduled_runs` + - [ ] `test_tokens_used_defaults_to_zero_for_error_logs` + - [ ] `test_error_log_action_retry_returns_wizard_action` + - [ ] `test_success_log_does_not_expose_retry_button` + +- [ ] `itsulu.blog.post.social` — social media copy + - [ ] `test_social_record_is_linked_one_to_one_with_blog_post` + - [ ] `test_twitter_posts_stored_and_retrievable` + - [ ] `test_twitter_posts_are_within_character_limit` + - [ ] `test_linkedin_post_is_at_least_150_characters` + - [ ] `test_mastodon_post_is_within_character_limit` + +- [ ] `itsulu.blog.schedule` — cron slot configuration + - [ ] `test_schedule_slot_is_created_with_correct_defaults` + - [ ] `test_disabled_schedule_slot_has_active_false` + - [ ] `test_three_distinct_slot_values_are_valid` + - [ ] `test_active_slot_run_creates_blog_post` + - [ ] `test_active_slot_run_creates_generation_log_with_success_state` + - [ ] `test_auto_publish_true_publishes_the_created_blog_post` + - [ ] `test_auto_publish_false_leaves_the_created_blog_post_as_draft` + - [ ] `test_inactive_slot_run_does_not_create_blog_post` + - [ ] `test_slot_picks_next_topic_from_queue_when_available` + - [ ] `test_slot_falls_back_to_llm_chosen_topic_when_queue_is_empty` + +### Layer 2: Services (LLM Integration) +- [ ] `llm_router.py` — dispatcher to provider APIs + - [ ] `test_router_response_includes_tokens_used` + - [ ] `test_anthropic_provider_calls_anthropic_endpoint` + - [ ] `test_openai_provider_calls_openai_endpoint` + - [ ] `test_gemini_provider_calls_gemini_endpoint` + - [ ] `test_ollama_provider_calls_local_api_endpoint` + - [ ] `test_unknown_provider_raises_user_error` + - [ ] `test_missing_api_key_raises_user_error` + +### Layer 3: Integration (E2E Flows) +- [ ] Generation orchestration + - [ ] `test_generate_and_autopublish_a_blog_post_from_the_backend` + - [ ] `test_generate_a_blog_post_and_leave_it_as_draft` + - [ ] `test_llm_api_call_fails_gracefully` + - [ ] `test_active_morning_slot_creates_a_blog_post_when_run` + +- [ ] SEO population + - [ ] `test_generated_post_has_non_empty_meta_title` + - [ ] `test_generated_post_has_non_empty_meta_description` + - [ ] `test_generated_post_has_meta_keywords` + - [ ] `test_generated_post_has_at_least_two_tags` + - [ ] `test_new_tags_from_llm_are_created_automatically` + - [ ] `test_seo_title_is_not_identical_to_post_title` + +- [ ] Notification email + - [ ] `test_notification_email_is_sent_after_auto_publish` + - [ ] `test_notification_email_is_not_sent_for_draft_posts` + - [ ] `test_notification_email_subject_matches_expected_format` + - [ ] `test_notification_email_body_contains_post_url` + - [ ] `test_notification_email_body_contains_all_social_platforms` + +- [ ] Provider-specific tests + - [ ] `test_anthropic_provider_generates_blog_content` + - [ ] `test_openai_provider_generates_blog_content` + - [ ] `test_gemini_provider_generates_blog_content` + - [ ] `test_ollama_provider_generates_blog_content_using_local_model` + +## Daily TDD Rhythm + +``` +Morning (RED): + - Pick next failing test + - Read test specification + - Understand what model/method is missing + +Midday (GREEN): + - Write minimum code to pass the test + - Run test locally (K8s job): pytest addons/itsulu_blog_publisher/tests -k -v + - If fails, iterate + +Afternoon (REFACTOR): + - Clean up code style + - Run full test suite: pytest addons/itsulu_blog_publisher/tests/ -v + - Pre-commit: black, isort, pylint-odoo + - Commit with message: "feat: implement (test: )" +``` + +## Success Criteria + +✅ **Phase 2 Complete when:** +- [ ] All 64 tests passing +- [ ] Coverage ≥ 80% on new code +- [ ] Pre-commit hooks pass +- [ ] CI pipeline green (lint + test + build) +- [ ] README updated with feature list +- [ ] Ready for Phase 3 (Runboat E2E, performance testing) + +## Current Progress + +- Infrastructure: ✅ Complete (K8s, Docker, CI/CD) +- Template DB: 🔄 Priming in progress +- Model implementations: 0/64 tests passing +- Code coverage: 0% + +--- + +**Next Step:** Verify template DB has models installed, then start with `test_blog_topic.py` (simplest model). diff --git a/addons/itsulu_blog_publisher/__manifest__.py b/addons/itsulu_blog_publisher/__manifest__.py index e094acf..5db6227 100644 --- a/addons/itsulu_blog_publisher/__manifest__.py +++ b/addons/itsulu_blog_publisher/__manifest__.py @@ -44,7 +44,6 @@ Features 'views/blog_schedule_views.xml', 'views/blog_generation_log_views.xml', 'views/blog_post_social_views.xml', - 'views/res_config_settings_views.xml', 'views/website_blog_publisher_templates.xml', 'views/menu_views.xml', ],