160 lines
4.0 KiB
Markdown
160 lines
4.0 KiB
Markdown
# 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.
|