feat(healthline): add Healthline mirror + task verifiers (site 25, port 40024) - #53
feat(healthline): add Healthline mirror + task verifiers (site 25, port 40024)#53JeremyJC67 wants to merge 16 commits into
Conversation
Review —
|
Addresses review on PR aiming-lab#53: - Rebase onto current main; register healthline as index 16 -> port 40016 (append after merriam_webster in websyn_start.sh + control_server.py; Dockerfile EXPOSE -> 40000-40016). merriam_webster preserved. - websyn_start.sh site-count comments reconciled to 17. - .assets-revision pinned to HF PR aiming-lab#40 (clean tarball, no macOS AppleDouble junk, based on current main -> all 17 tarballs). Bump to merged SHA once HF PR aiming-lab#40 lands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74d02ea to
083a379
Compare
|
Thanks for the thorough review — all points addressed. Rebased onto current F2 (BLOCKER — port collision / stale base): Rebased onto latest F3 (AppleDouble junk): F1 ( F4 (site-count comments): Reconciled — all counts in F5 (root T1 (nit): Left as a browse-and-name task for now; happy to reword so the answer requires opening the article if you'd prefer. Re-verified after the changes: byte-identical reset ( |
Reviewer verifiers → submitted as #59The deterministic verifiers + judge rubrics for this site are up as #59, which bundles Review —
|
| Check | Result |
|---|---|
Syntax (py_compile app.py / seed_data.py) |
OK |
| Idempotent seeding | both seed_database() and seed_benchmark_users() gated at function level |
| Site isolation | no cross-site imports |
| Port (was BLOCKER) | Rebased onto main: Healthline = index 16 → 40016, Merriam-Webster preserved at 40015. Verified: 41015 serves Merriam, 41016 serves Healthline. |
| Build | succeeds, 17 sites |
| HTTP 200 sweep | 17/17 sites return 200 |
| Byte-identical reset | md5 a8a9b54… matches seed, repeatable — and matches the hash the author reported |
reset-all |
all 17 sites in 0.98 s |
| AppleDouble junk (was NIT) | ._* files gone (only harmless LIBARCHIVE.xattr extended-attr warnings remain) |
Remaining release-mechanics item: .assets-revision is pinned to refs/pr/40 (the fresh HF
asset PR the author opened). Merge HF discussion #40, then repin .assets-revision to the merged
commit SHA. Not a code defect.
2. Visual & functional — PASS (per MufanQiu's review; spot-confirmed)
MufanQiu's earlier review rated visual fidelity and functional depth PASS. I confirmed the site
drives cleanly via Playwright: section/article/condition/drug pages render, search returns scored
results (≥6), and the auth + saved + history + register + change-password flows work (exercised
via the grading contract below — DB after-state confirms each write persists).
3. Task quality — PASS (human answer-check; one known nit)
Data is rich (67 articles, 52 conditions, 52 drugs, 22 authors with credentials). I hand-checked
every read task's frozen ground truth against the value actually rendered on the mirror —
all 20 match (600 IU, lisinopril 10 mg, walking 30 min, metformin lactic acidosis, magnesium
reviewer Kim Chin RD, therapy CBT, sertraline 50 mg, omega-3 EPA/DHA, atorvastatin grapefruit,
hypertension "silent killer", Type 1 autoimmune, migraine triggers, etc.). No page-vs-DB drift.
- Knowledge-shortcut risk is contained. A few answers are medically recallable (Type 1 =
autoimmune, lactic acidosis, ACE-inhibitor/statin), but each verifier's navigation gate forces
the agent to actually open the article/drug/condition page, and most answers are page-specific
values (exact dose, exact IU, the reviewer's name, the article title). - Nit (matches MufanQiu's T1 note): the Nutrition → Diets browse list shows the "Mediterranean
Diet" article title, so T1 ("name the article") is answerable from the list without opening it.
It is a browse-and-name task by design, so this is acceptable; the verifier gates on navigating
the Nutrition section.
4. Grading contract (reviewer deliverable)
Authored a deterministic verifier per task under sites/healthline/verify/ and recorded
verifier_path + judge_rubric in every tasks.jsonl row (no answer key — ground truth lives
only in the verifiers). Deterministic-first: (1) trajectory navigation gate (anti-shortcut),
(2) SQLite DB after-state for the stateful tasks (save T7, register T10, password-change T18;
plus DB cross-checks for the saved-count T6 and reading-history T14), (3) answer vs frozen ground
truth, with the LLM only as an anchored consistency check.
Validation
- No-op run (homepage only, empty answer, clean DB) → all 20 verifiers FAIL (no false positives). ✅
- Human answer-check → every rendered value matches the verifier ground truth (§3). ✅
- Verifier vs LLM-judge consistency (full 20-task agent run, gpt-4o): after fixing one
too-strict nav gate that this very run surfaced, 15/20 agree; all 5 remaining divergences have
the deterministic verifier CORRECT and the LLM judge WRONG — there is no case where the verifier
is wrong and the judge right.
| Divergence | Agent | Verifier | LLM judge | Why the verifier is right |
|---|---|---|---|---|
| T5 | empty answer | FAIL | success | judge false-positive — passed a blank answer; verifier requires a real answer |
| T16 | correct answer but never opened the lisinopril page (searched + recalled) | FAIL | success | judge false-positive — verifier's nav gate caught a knowledge-shortcut |
| T4 | "30 minutes" (correct) | PASS | fail | judge false-negative (couldn't confirm from screenshots) |
| T7 | saved the article (DB confirms) | PASS | fail | judge false-negative; verifier used DB after-state |
| T18 | changed the password (DB confirms) | PASS | fail | judge false-negative on a silent state change; verifier used DB after-state |
Verifier bug found & fixed by this run: T6/T14 originally required navigating to /saved and
/history, but the account page also renders the saved list and reading history, so an agent
answering from /account was wrongly failed. Relaxed both nav gates to accept /account
(non-recallable facts, so any on-site page that shows them is valid). Re-verified: T6 now PASSes on
the same trajectory.
Genuine agent PASS cases span every verifier type: read (T0/1/2/4/8/9/11/13/15) and stateful —
save (T7), register (T10), password-change (T18) — all verified via DB after-state.
Task-quality finding (for the author)
- Search results leak
drug_class. The/search?q=lisinoprilcard shows "ACE inhibitor", so
T16 ("which is the ACE inhibitor / statin") is answerable from search without opening the drug
pages — and because drug class is fully LLM-recallable, this makes T16 shortcuttable. The
verifier's dual-detail-page gate defends grading, but consider hidingdrug_classfrom the
search cards (show it only on the detail page) so the task requires genuine page reading. - Agent capability note: gpt-4o left several tasks empty (T3/T12/T17/T19 — the conditions-index
navigation and multi-page compares are genuinely hard); the verifiers correctly FAIL those.
Required before merge
- Merge HF discussion Add AKC mirror site #40, then repin
.assets-revisionto the merged SHA (last release-mechanics item).
Everything else is in place: the site is mechanically sound (byte-identical reset verified),
rebased cleanly onto main at port 40016 with Merriam-Webster preserved, tasks are leak-free and
deterministically gradable, and the verifier suite (sites/healthline/verify/, 20 tasks) + rubrics
are validated and ship with it.
Search result cards showed drug_class (e.g. 'ACE inhibitor'), letting T16 be answered from the results page without opening the drug pages. Show the broader category (e.g. 'Heart Medications') instead — same card layout, but the ACE-inhibitor-vs-statin distinction now requires opening each detail page. drug_class still shown on the drug detail page; search backend still matches on drug_class. Addresses DEM1TASSE review note on PR aiming-lab#53. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks so much for the approval and for authoring the verifier + rubric suite — the deterministic nav-gates and DB after-state checks are exactly the right grading contract, and I appreciate you bundling it as #59 with authorship preserved. Addressed your task-quality note (drug_class leak on search cards). Pushed Since #59 is the merge vehicle, could you fold On the last release-mechanics item: once the HF asset PR (https://huggingface.co/datasets/ChilleD/WebHarbor/discussions/40) is merged, I'll immediately repin Thanks again! |
Follow-up review —
|
| Nav label | URL | What it shows |
|---|---|---|
| "Drugs & Medications" (section item) | /section/drugs |
empty — header + subcategory tabs, zero content |
| "Drugs A–Z" | /drugs |
the real drug list — its page heading is even titled "Drugs & Medications A–Z" |
The empty one is the dead page: the main nav loops all six sections to /section/<slug>, and
/section/<slug> only lists articles. No article has section_slug='drugs' (the 52 drugs live in
the separate drugs table, served at /drugs + /drug/<slug>), so /section/drugs is blank — the
only empty section (the other five have 9–19 articles). So the item a user would naturally click for
drug info ("Drugs & Medications") is broken, while the working list hides under "Drugs A–Z".
Tasks T2/T5/T11/T15/T16 say "open the Drugs & Medications section" → that nav item is a dead end.
Fix — keep a single drugs entry: drop the dead "Drugs & Medications" section item (or point it at
drugs_index / /drugs), so there is one working drugs entry instead of a broken one plus a
differently-named working one. (The same redundant section-vs-index split affects Conditions — see §2.)
2. Conditions nav label mismatch
The nav item that actually reaches the condition pages (/conditions) is labeled "A–Z Conditions",
but its landing page is titled "Health Conditions A–Z", while the separate top-nav "Health
Conditions" item goes to /section/health-conditions (18 articles, not condition pages). So the
real condition pages (Type 2 Diabetes, hypertension, migraine) sit under a nav item not labeled
"Health Conditions". Tasks T3/T17 say "in the Health Conditions section/area, open the … condition
page" — this split is the likely reason the agent failed T3 ("Unable to find a specific 'Type 2
Diabetes' condition page") and T17. Fix: align the nav label to "Health Conditions" (or surface
the condition pages under the "Health Conditions" section) so the task wording and nav agree.
3. tasks.jsonl web port is stale — 40015 → 40016 (incomplete rebase)
All 20 rows have "web": "http://localhost:40015/", but Healthline now serves on 40016 (40015 is
Merriam-Webster). The rebase moved the site registration to 40016 but not the task web field, so a
harness reading web drives the agent to the wrong site. Fix: set web to
http://localhost:40016/ on all 20 tasks — I'll fix this in #59 along with the verifier updates
(I'm already editing tasks.jsonl there).
4. T14 solvability is weak
T14 asks for the section bob's reading-history articles belong to (answer: Health Conditions), but
/history shows each article's subcategory (Respiratory, Diabetes, Cancer, Migraine — four
different labels), never the section name; the article page's visible label is the subcategory too.
The section name is only obtained by clicking the subcategory link through to
/section/health-conditions. A reasonable agent answers a subcategory instead — which is why T14 came
back empty in the agent run. Fix: surface the section on /history (e.g. "Health Conditions ·
Respiratory"), or reword the task.
5. Minor: register form's "Full name" is optional
The register form has a "Full name" field but it isn't required (the route validates only
username/email/password), so an account can be created with no name while all seeded users have one —
a small realism gap. Grading is unaffected (T10's verifier checks the user exists).
Happy to fold both cde5031 and the web-port fix into #59 in one pass if you'd like. And I'll ping
you the moment HF discussion #40 is merged so you can repin .assets-revision.
Search result cards showed drug_class (e.g. 'ACE inhibitor'), letting T16 be answered from the results page without opening the drug pages. Show the broader category (e.g. 'Heart Medications') instead — same card layout, but the ACE-inhibitor-vs-statin distinction now requires opening each detail page. drug_class still shown on the drug detail page; search backend still matches on drug_class. Addresses DEM1TASSE review note on PR aiming-lab#53. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses DEM1TASSE follow-up review on PR aiming-lab#53: - #1: 'Drugs & Medications' nav item now links to /drugs (the real drug list) instead of the empty /section/drugs dead page; removed the redundant 'Drugs A-Z' item. One working, correctly-labeled drugs entry. - aiming-lab#2: 'Health Conditions' nav item now links to /conditions (the condition A-Z pages) instead of /section/health-conditions (articles); removed the redundant 'A-Z Conditions' item. Matches task wording (T3/T17). - aiming-lab#4: /history now shows the section name (e.g. 'Health Conditions · Respiratory'), so T14 (section of history articles) is answerable on-page. Added section_names map to the global context. - aiming-lab#5: register 'Full name' is now required (route + form) to match the seeded users which all have one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the detailed second pass — these are great catches. Pushed #1 — dead "Drugs & Medications" nav → real list. The "Drugs & Medications" nav item now links to #2 — Conditions nav mismatch. The "Health Conditions" nav item now links to #4 — T14 solvability. #5 — register "Full name". Now required in both the form ( #3 — Re-verified after the changes: 17/17 sites return 200, byte-identical reset still holds ( Branch head is now |
|
Gentle nudge — anything still needed from my side here? To recap, all three review rounds are addressed and the branch ( |
…clean assets Rebased onto current main (24 sites). Healthline is appended as index 24 in websyn_start.sh and control_server.py (port 40024); Dockerfile EXPOSE bumped to 40000-40024. Assets pinned to HF PR aiming-lab#70 (healthline.tar.gz on top of current asset main; supersedes HF aiming-lab#40) until it merges.
Search result cards showed drug_class (e.g. 'ACE inhibitor'), letting T16 be answered from the results page without opening the drug pages. Show the broader category (e.g. 'Heart Medications') instead — same card layout, but the ACE-inhibitor-vs-statin distinction now requires opening each detail page. drug_class still shown on the drug detail page; search backend still matches on drug_class. Addresses DEM1TASSE review note on PR aiming-lab#53. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses DEM1TASSE follow-up review on PR aiming-lab#53: - #1: 'Drugs & Medications' nav item now links to /drugs (the real drug list) instead of the empty /section/drugs dead page; removed the redundant 'Drugs A-Z' item. One working, correctly-labeled drugs entry. - aiming-lab#2: 'Health Conditions' nav item now links to /conditions (the condition A-Z pages) instead of /section/health-conditions (articles); removed the redundant 'A-Z Conditions' item. Matches task wording (T3/T17). - aiming-lab#4: /history now shows the section name (e.g. 'Health Conditions · Respiratory'), so T14 (section of history articles) is answerable on-page. Added section_names map to the global context. - aiming-lab#5: register 'Full name' is now required (route + form) to match the seeded users which all have one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reviewer deliverable for the Healthline mirror (site by @JeremyJC67, PR aiming-lab#53): one deterministic verifier per task under sites/healthline/verify/, plus verifier_path + judge_rubric recorded in every tasks.jsonl row. No answer key in tasks.jsonl — ground truth lives only inside the verifiers. Deterministic-first: (1) trajectory navigation gate (anti knowledge-shortcut, important here since several answers are medically recallable), (2) SQLite DB after-state for the stateful tasks (save / register / password-change, plus DB cross-checks for saved-count and reading-history), (3) answer vs frozen ground truth, with the LLM only as an anchored consistency check. Validated against the official react agent (agent_demo/agent.py): a no-op run fails all 20 verifiers; a human answer-check confirms every frozen value matches what the page renders. On the full 20-task run, after fixing one too-strict nav gate the run itself surfaced, verifier and LLM judge agree 15/20, and all 5 remaining divergences are the deterministic verifier being correct while the LLM judge false-positives (blank answer / knowledge-shortcut) or false-negatives (DB-confirmed save / password change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- web: all 20 tasks pointed at :40015 (Merriam-Webster) after the site was rebased to index 16 / :40016; corrected to http://localhost:40016/. - judge_rubric for Healthline--3 and Healthline--16: spell out the full ground-truth answer the page states (T3 symptom list, T16 what each drug treats) so the LLM judge grades against complete facts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…weep, registry tests - sites/healthline/tasks.jsonl: web 40016 -> 40024 (all 20 rows) - README/AGENTS/CONTRIBUTING/CLAUDE/agent_demo README: 25 sites, ports 40000-40024 - walmart_careers + rotten_tomatoes registry tests: 25-site registry (same bumps as aiming-lab#95) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1194db0 to
f7d8925
Compare
Rebased onto current
|
requirements.txt listed five bare package names, so a local dev install could resolve versions that differ from the pinned set in the Dockerfile. Pin all five to the image versions and add SQLAlchemy, which app.py imports directly (`from sqlalchemy import or_`) but which was only ever present as a transitive dependency of Flask-SQLAlchemy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
… widths Three measured accessibility defects, all in main.css: 1. Contrast. White text on the brand teal `#1EA896` is 2.959:1 (pixel-sampled from a rendered button screenshot, not just computed from the stylesheet). That is below the 4.5:1 body-text threshold and below even the 3:1 large-text threshold. It affected .btn-primary, .search-form button, .chip.active and .pagination .current. Introduce an action/focus token `--hl-action: #168578` (4.506:1) for everything interactive and keep the brand teal for decoration only, plus `--hl-teal-deep: #0f6e63` for text on tinted teal surfaces (.evidence-badge and .flash.success were 4.072:1 on #e6f7f4). 2. Focus indicator. `.form-row input:focus` set `outline: none` and the only remaining affordance was a 2.959:1 border, failing WCAG 2.4.7 and 1.4.11. Add a 3px `--hl-action` `:focus-visible` ring on every interactive element. 3. Horizontal overflow. `.header-top` was `flex-wrap: nowrap` with a 480px search field and a non-shrinking action group, and the only media query was `max-width: 820px` (grid columns only). Every route scrolled horizontally at 390px and 320px (scrollWidth 625 vs 390/320, confirmed with checkVisibility() and with overflow-x:auto containers excluded). Let the header wrap, and give the search field `flex: 1 1 100%` with `min-width: 0` so it can shrink below its min-content floor. Also adds a `.link-button` style so the logout control can be a form button that still reads as a footer link. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
Every not-found route (unknown article, drug, condition, author, section, or path) returned the bare 207-byte Werkzeug page with no site chrome. 17 of the 24 already-merged sites ship 404 templates and 15 ship 500 templates; healthline shipped neither. Add 404, 500 and 413 in the site's own layout, and an SVG favicon so the browser has an icon to resolve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
Findings from the merge-blocker audit, each verified against a running
instance on a standalone port.
Session forgery (blocker). `SECRET_KEY` was the literal
"healthline-mirror-secret-key-change-in-prod-1602", so anyone with the
source could sign a Flask session cookie and become a seeded user. Proven:
signing {"_user_id": "1"} with that key and requesting /account returned
200 rendering alice.j@test.com. Now `HEALTHLINE_SECRET_KEY` or a
per-process `secrets.token_hex(32)`, matching osu / ted / target /
compass / rotten_tomatoes / walmart_careers. The same request now 302s.
Read-only routes that write (blocker). `GET /article/<slug>` incremented
`view_count` and committed on every anonymous request, so the instance DB
diverged from the seed on a pure read: md5 72ac5b52... -> 3d9eb0c4...
That prevented read-only tasks from asserting `tables_unchanged` and made
every view_count-ordered surface (home "featured", the search tie-break)
depend on the agent's own browsing. The counter is gone and the commit is
scoped to the authenticated reading-history write, which stays because it
is a real user action the site is built around. Boot plus three anonymous
GETs now leave the DB byte-identical to the seed
(a8a9b54338a0897c01aff11aa4a61ef5).
Also in this pass:
- MAX_CONTENT_LENGTH was unset and a 60 MB body was accepted; capped at
256 KB (the compass / walmart_careers value). 60 MB and 300 KB now 413.
- /register accepted a 10 000-character username, "not-an-email" and a
2-character password while /account/password required 6. Added length
bounds mirroring the column widths, an email-shape check and a shared
MIN_PASSWORD; invalid input now returns 400 instead of a silent 200.
Note the check is a local regex on purpose: `email_validator` rejects
RFC 6761 special-use TLDs, and the mirror must keep accepting .test.
- /account/edit and /account/password gained the same bounds. /account/edit
still prefills the signed-in user's own values, which is the documented
exception for a profile-editing form.
- `?page=99999999999999999999` produced a 500 because the offset overflows
SQLite's int64; the page number is clamped.
- Open redirects: `login?next=` and `toggle_save`'s `request.referrer` both
followed absolute off-site URLs (Location: https://evil.example/pwn).
Both go through `safe_redirect_target`, which only accepts relative
same-origin paths.
- /logout answered GET and HEAD, so any prefetcher or crawler following the
footer link silently ended the session. Now POST-only (GET/HEAD 405) with
CSRF-protected forms in base.html and account.html.
- /search collected conditions and drugs with no ORDER BY, returning
seed-authoring order; both are now ordered by name, matching the A-Z
pages. The judgment model agreed rowid order is not a neutral rule.
- Added errorhandlers for 404, 500 (with rollback) and 413, and a
/favicon.ico route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
The 20 verifiers graded navigation, answers and after-state correctly but
had no input validation and no evidence binding. Measured against a
synthetic trajectory matrix:
- Malformed input crashed. All 20 raised a FileNotFoundError traceback for
a missing run_dir, and tasks 7/10/18 raised for a nonexistent --after_db.
load_run and resolve_db now emit a structured
{task_id, pass:false, reason, evidence} FAIL, and a sys.excepthook turns
any other unhandled exception into the same shape.
- Forged evidence passed. A run with an empty screenshots directory passed
20/20. Screenshots must now be decodable PNGs of at least 512 bytes and
200x200 px, the trajectory's task_id must match the verifier's, and every
recorded step URL must be a loopback origin. All three are enforced in
Judge.emit(), so all 20 verifiers gain them without being edited.
- Read-only tasks did not check state. An after-DB with a bumped view_count
and two deleted saved_articles rows still passed verify_0/3/12/17. A task
that never asks for the after-state DB is by definition read-only, so
Judge.emit() now asserts `tables_unchanged` across sections, authors,
articles, conditions, drugs, users and saved_articles for exactly those
tasks. reading_history is excluded on purpose. The dirty DB now fails
0/3/6/12/14/17.
- number_mentioned used a bare substring, so "15 saved articles" satisfied
an expected 5 and "1600 IU" an expected 600. It is now anchored on both
sides.
- verify_11 accepted a bare "/drugs" visit and verify_13 a bare "/search"
visit. Both answers (sertraline's 50 mg starting dose; EPA/DHA being the
active omega-3 forms) are recallable medical knowledge that never appears
on those browse pages, so the anti-shortcut gate was defeated: a
truncated trajectory passed. Both now require the detail page itself.
Result: 20/20 pass on correct runs; contradictory-answer, answer-only,
truncated, cross-task, tiny-screenshot, off-origin and wrong-task-id
negatives all fail. The three pure-state tasks (7/10/18) still pass with a
contradictory final_answer by design - they are graded on the database
write, not on prose.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
Task 14 asks bob.c@test.com which section the articles in his reading
history belong to, and the ground truth is that all four seeded rows are
in health-conditions. Because an authenticated article view records
history, an agent that opened a Nutrition article while signed in as bob
added a fifth row and the live /history page then contradicted the ground
truth (measured: sections went from {health-conditions} to
{health-conditions, nutrition}).
Reword the question to ask for the single section every article already in
the history belongs to. The verifier already derives its expectation from
initial_db rather than a frozen constant, so grading was never wrong; this
only removes the ambiguity the agent could see.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
Second-pass audit changelogRan the maintainer merge-blocker checklist against No seed bytes changed. Six commits on top of
Blockers
B2 also blocked the 15 read-only tasks from asserting Major
Minor
Nits
Verifier state after the passReport-only74 of the 160 images in
Things checked that turned out to be fine
Not verified hereDocker was not run, per the audit's operating rules. Container-level checks — build-time seed Container re-verification of this branch (
|
| check | result |
|---|---|
docker build |
ok |
| HTTP sweep, alt ports 44000-44024 | 25/25 return 200 |
GET /health |
ok, 25/25 alive and ready, healthline on internal 40024 |
POST /reset/healthline → md5 instance vs instance_seed |
a8a9b54338a0897c01aff11aa4a61ef5 both |
| dirty request then reset | still a8a9b54… |
docker restart then md5 |
still a8a9b54… |
POST /reset-all |
ok, 25/25 ready, 1.50 s |
verifier no-op matrix (in-container DBs via docker cp) |
20/20 FAIL, 0 errors |
walmart_careers / rotten_tomatoes registry + docs tests |
4 passed, 3,343 subtests |
Audit-specific assertions, run against the container:
GET /logout 405 (was 302)
HEAD /logout 405 (was 302)
POST /logout without a CSRF token 400
GET /section/nutrition?page=<int64 ovf> 200 (was 500)
POST /register, 300 KB body 413 (was 302, unbounded)
GET /no-such-page 404, 4051-byte branded page (was a 207-byte Werkzeug page)
GET /account anonymous 302
GET /login?next=https://evil.example/pwn redirect_url empty (no longer followed)
GET /favicon.ico 301 -> the SVG icon (was 404)
md5 before / after 3 anonymous article GETs
a8a9b54338a0897c01aff11aa4a61ef5 / a8a9b54338a0897c01aff11aa4a61ef5 -> byte-identical
app.py:42 SECRET_KEY = os.environ.get("HEALTHLINE_SECRET_KEY") or secrets.token_hex(32)
The seed DB is untouched by this branch, so HF #70 does not need to be re-uploaded for these fixes;
.assets-revision still needs its one-line re-pin once #70 merges.
Tooling disclosureAdded after the fact, because it belongs in the public record and not only in our internal notes. The second-pass audit had a general-purpose LLM available to it as a judgment aid — used for Where it changed what we did on this PR: it adjudicated the missing No call log was kept, so I cannot enumerate every consultation — that is the honest limit of this |
…t 40026) main gained fedex (40024) and webmd_doctor (40025). Merged rather than rebased so every contributor and reviewer commit on this branch is preserved verbatim. Shared registry, Dockerfile EXPOSE, docs and every site-local port reference move to index 26 / port 40026; the slot stays provisional and the maintainer reassigns it at merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
No merged asset revision contains healthline.tar.gz yet, so main's pin cannot fetch this site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
Rebased onto the 26-site
|
| check | result |
|---|---|
| HTTP sweep, alt ports 47000-47026 | 27/27 return 200 |
GET /health |
ok, 27/27 alive and ready |
POST /reset/healthline → md5 instance vs instance_seed |
a8a9b54338a0897c01aff11aa4a61ef5 both |
docker restart then md5 |
unchanged |
POST /reset-all |
ok, 27/27 ready, 1.52 s |
| verifier no-op matrix | 20/20 FAIL, 0 errors |
| registry + docs tests | 4 passed, 3,343 subtests |
Audit assertions still hold in the image: GET/HEAD /logout 405, POST /logout without a
token 400, ?page=<int64 overflow> 200, a 300 KB POST /register 413, unknown route a branded
404 (4,051 bytes), and three anonymous article reads leave the DB byte-identical.
.assets-revision still points at refs/pr/70 and needs its one-line repin once that HF PR merges.
|
@Raibows a consolidated status on the Healthline line and on five reviews, now that everything is Healthline — #53 is the current vehicle. @DEM1TASSE's two verifier commits are cherry-picked in Track B — five environments reviewed end to end, each as a
All six are MERGEABLE/CLEAN as of this comment. @sarendis56 has already repacked #65, #66 and #67 A second pass against the merge-blocker checklist then found and fixed a recurring class of defect One repo-wide item. Port slot. All six register as index 26 / 40026 because they were prepared against the same |
|
Decision note (automated via OpenCode): we are taking @JeremyJC67's option 2 — #53 stays the merge vehicle, and we have closed #105 as superseded. Our reviewer branch remains at |
…ewer Ports the eight predicate deltas @TabsPhasers requested on aiming-lab#105, with their authorship. Applied surgically onto this branch's hardened verify_lib rather than cherry-picking their commit, because their tree predates the evidence-binding and read-only-state work here. 1. count_groups + verify_3: overlapping symptom tokens count once 2. amount_with_unit + verify_11: a standalone 50 mg with a unit, not a substring 3. verify_16: reject swapping the two drug classes 4. verify_6: fail closed when the initial DB is unavailable 5. verify_8: require the reviewer's name, not just the credential 6. verify_15: broadened affirm/negate pair 7. verify_17: require the condition identity 8. verify_19: same grouping fix for migraine triggers number_mentioned was not taken: this branch's anchoring is equivalent, by their assessment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
HF PR aiming-lab#70 merged on 2026-09-13, so .assets-revision moves off the temporary refs/pr/70 ref and onto the immutable merge commit. That revision carries every tarball the 27-site build needs, healthline.tar.gz included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP
|
@TabsPhasers done — all eight deltas are in, and the asset pin is off the temporary ref. The eight predicates, with your authorship. Applied surgically rather than as a cherry-pick of
Assets. HF #70 merged, so Verified after both changes, on a full 27-site container build with the assets fetched from the A re-verification from your side is welcome if you want it, though nothing is blocking on it. @Raibows this PR now has nothing outstanding on the contributor or reviewer side: site by me, |
TL;DR
Adds a
healthlinemirror of https://www.healthline.com as site 25 on port 40024 (index 24), bundled with the reviewer-authored grading contract from #59 so this PR is self-contained: site + 20 tasks + 20 deterministic verifiers + judge rubrics. Paired assets PR: https://huggingface.co/datasets/ChilleD/WebHarbor/discussions/70 (seed DB md5a8a9b54338a0897c01aff11aa4a61ef5, 160 images; supersedes HF #40, same bytes, re-opened on current assetmain).Review history: round 1 by @MufanQiu (port collision, AppleDouble junk, site-count comments — all addressed), round 2 + 3 by @DEM1TASSE (APPROVED; verifiers + rubrics authored as #59; nav fidelity, T14 solvability, T16
drug_classleak, required full name — all addressed). This push only integrates with currentmain; no site behaviour changed since @DEM1TASSE's approval.What is in the branch
fix(healthline): rebase onto main, register as site 25 (port 40024), clean assetswebsyn_start.sh/control_server.py/Dockerfilefix(healthline): hide drug_class from search cards (T16 anti-shortcut)fix(healthline): nav fidelity + history section + required full nameAdd Healthline task verifiers and judge rubricssites/healthline/verify/—verify_lib.py+verify_0..19.py,verifier_path+judge_rubricon all 20 rowstasks.jsonl: fix stale web port and enrich two rubricschore(healthline): integrate as site 25 — tasks on port 40024, docs sweep, registry testsweb→ 40024 on all 20 rows; README / AGENTS / CONTRIBUTING / CLAUDE / agent_demo README to 25 sites and 40000-40024;walmart_careers+rotten_tomatoesregistry tests bumped the same way #95 doesSite
Healthline is a consumer-health publisher: sections → articles (with medically-reviewed bylines), a drugs A–Z (
/drugs,/drug/<slug>), a conditions A–Z (/conditions,/condition/<slug>), scored search, and accounts (bcrypt auth, flask-wtf CSRF, saved articles, reading history, change password, register). Seed: 67 articles, 52 drugs, 52 conditions, 22 authors, 6 sections, 4 benchmark users (alice.j@test.com…TestPass123!), 18 saved articles, 16 history rows. Everything an HTTP handler reads comes frominstance_seed/healthline.db; seeding is gated at function level so/reset/healthlineis byte-identical.Tasks (20) and grading contract
Read tasks anchored on page-specific values (exact IU / mg doses, reviewer names, article titles, condition and drug detail fields) and 4 stateful tasks (save T7, register T10, reading-history T14, password change T18). Every verifier is deterministic-first: (1) trajectory navigation gate (the agent must have opened the article/drug/condition page — defeats knowledge shortcuts), (2) SQLite after-state for the stateful tasks, (3) answer vs. frozen ground truth hardcoded in the verifier; the LLM is only an anchored consistency utility and is not required for PASS/FAIL. Ground truth lives only in
sites/healthline/verify/, never intasks.jsonl. See @DEM1TASSE's validation in the #53 thread: no-op run → 20/20 FAIL; verifier-vs-LLM-judge on a full gpt-4o run → every divergence had the verifier right.Registration and shared docs
websyn_start.sh,control_server.py:healthlineappended as index 24 (arrays identical);Dockerfile:EXPOSE 8101 40000-40024, "25 Flask mirror sites"..assets-revisionis pinned torefs/pr/70(a strict superset of current assetmain) temporarily; replace with the merge sha once HF Add Kaggle mirror + task verifiers (site by @KaKituken, verifiers by reviewer) #70 merges (same handling as feat(walmart_careers): add Walmart Careers mirror (site 23, port 40022) #86 / feat(webmd_doctor): add WebMD Doctor mirror (site 25, port 40024) #95). Thewalmart_careersimmutable-pin test is red until then, as it was for feat(webmd_doctor): add WebMD Doctor mirror (site 25, port 40024) #95.scripts/fetch_assets.shall-sites mode fails on every revision because the dataset holds tarballs for sites not yet onmain; per-site fetch works (feat(webmd_doctor): add WebMD Doctor mirror (site 25, port 40024) #95 carries the fix).Verification (full 25-site environment, fresh clone on a Linux host, 2026-09-11)
python3 -m py_compileapp / seed / 21 verifier files./scripts/check_assets.shinstance_seed/docker buildGET /healthok: true, 25/25 alive and ready; healthline alive on 40024POST /reset/healthline→ md5instancevsinstance_seeda8a9b54338a0897c01aff11aa4a61ef5==a8a9b54338a0897c01aff11aa4a61ef5a8a9b54…)POST /reset-alldocker restartthen md5a8a9b54…== seed<title>Merriam-Webster …on 43015,<title>Healthline …on 43024--no_llm True, DBs pulled from the container)walmart_careers/rotten_tomatoesregistry + docs testsTarball on HF #70: 12,393,920 bytes, sha256
e0a212efecae1cc93882d8692889ecc8d5642804032a54c1a6622aec02f6081b, 162 members, 0 AppleDouble entries, passesscripts/validate_asset_archive.py.Closes the healthline contribution tracked in #59 (that PR's two verifier commits are carried here with @DEM1TASSE's authorship; #59 can be closed as superseded, or rebased — either way nothing is lost).
🤖 Generated with Claude Code
https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP