Skip to content

feat: wire layer-3 hooks + CI doctor check + test coverage - #9

Merged
Null-Phnix merged 3 commits into
mainfrom
glm/v1.4-hooks-and-cleanup
Jul 12, 2026
Merged

feat: wire layer-3 hooks + CI doctor check + test coverage#9
Null-Phnix merged 3 commits into
mainfrom
glm/v1.4-hooks-and-cleanup

Conversation

@Null-Phnix

Copy link
Copy Markdown
Owner

v1.4 — Layer-3 hooks wiring + cleanup

What changed

Layer-3 hooks wired into Scraper.scrape() — the main "NOT done" item from v1.3:

  • Adaptive throttling: record_outcome(domain, success=True/False) called after every scrape, feeding the per-domain rate multiplier so it backs off on failures and recovers on successes.
  • Selector memory: successful include_tags are recorded per-URL; when future scrapes of the same URL omit include_tags, selector_hints appear in metadata with scored suggestions.

CI: huginn doctor --check added as an informational step in the test workflow.

Bug fix: api.pydt.fromisoformat()datetime.fromisoformat() (undefined dt alias).

Tests: 8 new tests in tests/test_layer3_hooks.py covering both hooks. Fixed test_lightweight_scrape_also_includes_language mock to set last_status_code.

Test count

  • Before: 704 passed
  • After: 712 passed (8 new), 6 deselected, 1 warning

Commits

  1. feat(scraper): wire layer-3 hooks — adaptive throttling + selector memory
  2. ci: add huginn doctor --check step to test workflow
  3. test(scraper): add layer-3 hook wiring tests + fix language detection mock

Null-Phnix and others added 3 commits June 17, 2026 15:44
…mory

The adaptive throttling and selector memory modules were added in
PR #8 (v1.3) as standalone, tested modules — but the scraper never
called them. This commit wires them into the real scrape path.

Adaptive throttling (DomainRateLimiter.record_outcome):
  - After a successful scrape: record_outcome(domain, success=True)
    so the rate multiplier can recover from past failures (+0.05
    per success, ceiling 1.0).
  - After a failed scrape: record_outcome(domain, success=False)
    so the multiplier backs off (×0.7, floor 0.1).
  - The scraper already had rl = self._rl or get_domain_rate_limiter()
    so the limiter is the same instance the rest of the code uses.

Selector memory (SelectorMemory):
  - When include_tags is provided and the scrape succeeds, each
    selector is recorded via record_success(url, selector).
  - When include_tags is empty, suggestions from memory are surfaced
    in result.metadata['selector_hints'] as {selector, score} pairs.
  - Lazy singleton via get_selector_memory(); tests can override
    scraper._selector_memory directly.

Also fixes api.py dt.fromisoformat → datetime.fromisoformat (the
import was 'from datetime import datetime, timezone' but the code
used the 'dt' alias which was never imported in that scope).
Runs the deep health check after unit tests. Uses '|| true' because
the check may WARN on missing LLM credentials in CI (expected — the
server can run scrapes without an LLM key). The step is informational
for now; it surfaces broken Playwright or webhook signing issues in
the CI log without failing the build.
… mock

8 new tests verifying adaptive throttling (record_outcome called
with success=True/False) and selector memory (records successful
include_tags, surfaces hints when include_tags empty, skips on
error status, no hints when memory empty).

Fix: test_lightweight_scrape_also_includes_language didn't set
mock_browser.last_status_code, causing TypeError when layer-3 hooks
compared status_code < 400. Added explicit last_status_code=200.
@Null-Phnix
Null-Phnix merged commit 6834354 into main Jul 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant