## Implementation Plan ### Objective Reduce the 55% job failure rate in SEO Diagnostics by rejecting malformed URLs and relative paths at submission time, consistent with the existing `IsslopRunForm` validation pattern. ### Changes Required **1. Add URL validation to `SeoRunForm`** File: `devplacepy/models.py`, lines 456–462. Replace the current `url_scheme` validator with the same logic used by `IsslopRunForm` (lines 470–482). Specifically: - Strip the input. - Fail if empty. - If the input does not match `ISSLOP_SCHEME_PATTERN`, prepend `https://`. - If the resulting string does not match `ISSLOP_URL_PATTERN`, raise `ValueError("URL must be an http(s) location")`. This will reject `ahttps://devplace.net/sitem` (fails URL pattern), `/feed` (becomes `https:///feed`, fails pattern due to triple slash or missing host), and any other malformed input. **2. Add unit tests for malformed & relative URL rejection** File: `tests/api/tools/seo.py`. Add two test cases to `test_run_rejects_blank_url` suite (or as new functions): - `test_run_rejects_malformed_scheme` — POST with `"ahttps://devplace.net/sitem"`, expect 400/422. - `test_run_rejects_relative_path` — POST with `"/feed"`, expect 400/422. **3. (Optional but recommended) Add e2e test for form-level feedback** File: `tests/e2e/tools/seo.py`. Add test submitting a malformed URL and verifying an error message is shown in the `data-seo-error` element. This can be deferred if e2e infrastructure is slow, but it closes the disputed “error feedback absent” gap. **4. Verify no regression in valid URL submissions** Ensure existing test `test_run_enqueues_and_returns_uid` still passes (it uses `https://example.com`). ### Definition of Done All of the following must be true: - [ ] `devplacepy/models.py` is updated: `SeoRunForm.url_scheme` now uses `ISSLOP_SCHEME_PATTERN` and `ISSLOP_URL_PATTERN` to validate the URL, rejecting malformed inputs. - [ ] Two new unit tests exist in `tests/api/tools/seo.py` that verify POST requests with `ahttps://devplace.net/sitem` and `/feed` return HTTP 400 or 422. - [ ] All existing unit tests pass: `pip install -e '.[dev]' -q && make test-unit` exits with code 0. - [ ] Lint passes: `pip install -q ruff && ruff check .` exits with code 0. - [ ] The change is limited to the two files above (plus optionally the e2e test file). No new dependencies are introduced. ### Execution Notes - The verification commands (`pip install -e '.[dev]' -q && make test-unit`, `pip install -q ruff && ruff check .`) must be run from the repository root. If the environment restricts system-level pip (PEP 668), use a virtualenv (e.g., `python3 -m venv .venv && source .venv/bin/activate` before running the commands). The definition of done refers to the exact commands as stated; pass them in an allowed environment.