## Implementation Plan: Add URL Format Validation to SEO Diagnostics Job Queue ### 1. Modify `devplacepy/models.py` **Current (committed) code in `SeoRunForm.url_scheme` validator (lines ~456–462):** ```python @field_validator("url") @classmethod def url_scheme(cls, value): text = value.strip() if not text: raise ValueError("A URL is required") return text # ← No scheme validation ``` **Changes to apply:** 1. Add import at top of file (preferably near existing imports): ```python from urllib.parse import urlparse ``` 2. Replace the validator body with: ```python @field_validator("url") @classmethod def url_scheme(cls, value): text = value.strip() if not text: raise ValueError("A URL is required") scheme = urlparse(text).scheme if scheme not in ("http", "https"): raise ValueError("URL must start with http:// or https://") return text ``` **Rationale:** `urlparse` extracts the scheme correctly for all edge cases: | Input | `.scheme` | Valid? | |---|---|---| | `"ahttps://devplace.net/sitem"` | `"ahttps"` | No → rejected | | `"/feed"` | `""` | No → rejected | | `"https://example.com"` | `"https"` | Yes | | `"http://example.com"` | `"http"` | Yes | | `" "` | — | Rejected by empty‑string check first | This catches both malformed schemes (`ahttps://…`) and relative paths (`/feed`) that would otherwise become malformed after `_normalize_url`. ### 2. Add Tests in `tests/api/tools/seo.py` Add two new test functions **after** the existing tests, following the same `try/finally`/`_clear_seo_jobs()` pattern. **Test 1 – `test_run_rejects_malformed_scheme`:** - POST to `/tools/seo/run` with `{"url": "ahttps://devplace.net/sitem", "mode": "url"}` - Expect HTTP 400 or 422 status code **Test 2 – `test_run_rejects_relative_path`:** - POST to `/tools/seo/run` with `{"url": "/feed", "mode": "url"}` - Expect HTTP 400 or 422 status code ### 3. Environment Fix for Verification Commands Earlier attempts failed because of PEP 668 (`pip install` blocked in system environment). To ensure the verification commands run, the agent **must** use one of these two approaches **before** running `pip install -e '.[dev]' -q`: - **Option A (preferred):** Create and activate a virtual environment: ```bash python -m venv .venv source .venv/bin/activate ``` - **Option B (fallback):** Prepend `PIP_REQUIRE_VIRTUALENV=false` or pass `--break-system-packages`: ```bash PIP_REQUIRE_VIRTUALENV=false pip install -e '.[dev]' -q ``` or ```bash pip install --break-system-packages -e '.[dev]' -q ``` Then run: ```bash make test-unit pip install -q ruff && ruff check . ``` --- ## Definition of Done - [ ] The `url_scheme` validator in `SeoRunForm` rejects `"ahttps://devplace.net/sitem"` with a `ValidationError`. - [ ] The validator rejects `"/feed"` with a `ValidationError`. - [ ] The validator accepts `"https://example.com"` and `"http://example.com"`. - [ ] Two new test functions exist in `tests/api/tools/seo.py` that verify the two rejection cases above. - [ ] `make test-unit` passes (all tests, including the two new ones, succeed). - [ ] `ruff check .` passes with no new linting errors. - [ ] No existing tests are broken or modified (only additive changes). - [ ] The environment is set up so that `pip install` does not fail due to PEP 668 (virtual env or `--break-system-packages`).