Refactor AI daily report pipeline

This commit is contained in:
Mimikko-zeus
2026-06-04 15:21:56 +08:00
parent 94e18ce22d
commit 5a98696255
64 changed files with 4778 additions and 1316 deletions

View File

@@ -0,0 +1,159 @@
# Local Dry-Run Foundation Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Make the current pipeline testable on a local machine without Hermes credentials, blog credentials, or live LLM calls.
**Architecture:** Keep the existing single script as the compatibility entrypoint. Add small, tested helpers for project `.env` loading, dry-run token behavior, and mock LLM responses. This creates a safe base for later Stage 0-8 modularization.
**Tech Stack:** Python standard library, `unittest`, current `script/ai_daily_blog_pipeline.py`.
---
### Task 1: Add Local `.env` Loading
**Files:**
- Modify: `script/ai_daily_blog_pipeline.py`
- Create: `tests/test_env_loading.py`
**Step 1: Write the failing test**
Test that `load_env()` reads project-root `.env` values when Hermes env is absent, and that real process environment variables override file values.
**Step 2: Run test to verify it fails**
Run: `python -m unittest tests.test_env_loading -v`
Expected: FAIL because the script currently only reads `~/.hermes/.env`.
**Step 3: Implement minimal code**
Add a helper to parse env files and update `load_env()` to read:
1. Project `.env`
2. `~/.hermes/.env`
3. process environment
Later sources override earlier ones.
**Step 4: Run test to verify it passes**
Run: `python -m unittest tests.test_env_loading -v`
Expected: PASS.
### Task 2: Let Dry-Run Skip Blog Token Requirement
**Files:**
- Modify: `script/ai_daily_blog_pipeline.py`
- Create: `tests/test_dry_run_config.py`
**Step 1: Write the failing test**
Extract a small helper such as `is_dry_run(env)` and `require_blog_token(env)`, then test:
- `AI_DAILY_DRY_RUN=1` does not require `BLOG_SERVICE_TOKEN`.
- normal publish mode still requires a token.
**Step 2: Run test to verify it fails**
Run: `python -m unittest tests.test_dry_run_config -v`
Expected: FAIL because no helper exists and `main()` checks token before dry-run.
**Step 3: Implement minimal code**
Move dry-run detection before token validation in `main()`.
**Step 4: Run test to verify it passes**
Run: `python -m unittest tests.test_dry_run_config -v`
Expected: PASS.
### Task 3: Add Mock LLM Mode
**Files:**
- Modify: `script/ai_daily_blog_pipeline.py`
- Create: `tests/test_mock_llm.py`
**Step 1: Write the failing test**
Test that `llm_call(prompt, {"AI_DAILY_LLM_MODE": "mock"})` returns valid JSON for:
- semantic dedup prompts
- summary rewrite prompts
- classify prompts
Also test that guide generation can get a non-empty mock response.
**Step 2: Run test to verify it fails**
Run: `python -m unittest tests.test_mock_llm -v`
Expected: FAIL because mock mode does not exist.
**Step 3: Implement minimal code**
Add `AI_DAILY_LLM_MODE=mock` support in `llm_call()`.
**Step 4: Run test to verify it passes**
Run: `python -m unittest tests.test_mock_llm -v`
Expected: PASS.
### Task 4: Add Markdown Smoke Test
**Files:**
- Create: `tests/test_markdown_rendering.py`
- Modify: `script/ai_daily_blog_pipeline.py` only if necessary.
**Step 1: Write the failing or characterization test**
Test that `blog_markdown()` renders:
- `## 导览`
- at least one section
- source links
- no `> >`
- no `[N]`
**Step 2: Run test**
Run: `python -m unittest tests.test_markdown_rendering -v`
Expected: If it already passes, keep it as characterization coverage. If it fails because of `> >`, implement a focused fix.
**Step 3: Implement minimal fix if needed**
Strip leading `>` from guide text before adding blockquote syntax.
**Step 4: Run test to verify it passes**
Run: `python -m unittest tests.test_markdown_rendering -v`
Expected: PASS.
### Task 5: Run Full Verification
**Files:**
- No new files.
**Step 1: Run unit tests**
Run: `python -m unittest discover -s tests -v`
Expected: PASS.
**Step 2: Run compile check**
Run: `python -m py_compile script/ai_daily_blog_pipeline.py`
Expected: exit code 0.
**Step 3: Check git status**
Run: `git status --short`
Expected: only intended files are modified or added.