Skip to content

feat(healthline): add Healthline mirror + task verifiers (site 25, port 40024) - #53

Open
JeremyJC67 wants to merge 16 commits into
aiming-lab:mainfrom
JeremyJC67:add-healthline-site
Open

feat(healthline): add Healthline mirror + task verifiers (site 25, port 40024)#53
JeremyJC67 wants to merge 16 commits into
aiming-lab:mainfrom
JeremyJC67:add-healthline-site

Conversation

@JeremyJC67

@JeremyJC67 JeremyJC67 commented Jun 21, 2026

Copy link
Copy Markdown

TL;DR

Adds a healthline mirror 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 md5 a8a9b54338a0897c01aff11aa4a61ef5, 160 images; supersedes HF #40, same bytes, re-opened on current asset main).

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_class leak, required full name — all addressed). This push only integrates with current main; no site behaviour changed since @DEM1TASSE's approval.

What is in the branch

commit author content
fix(healthline): rebase onto main, register as site 25 (port 40024), clean assets @JeremyJC67 the site: Flask app, 27 templates, seed_data.py, tasks.jsonl, registration in websyn_start.sh / control_server.py / Dockerfile
fix(healthline): hide drug_class from search cards (T16 anti-shortcut) @JeremyJC67 review round 2 fix
fix(healthline): nav fidelity + history section + required full name @JeremyJC67 review round 3 fixes
Add Healthline task verifiers and judge rubrics @DEM1TASSE (cherry-picked from #59, authorship preserved) sites/healthline/verify/verify_lib.py + verify_0..19.py, verifier_path + judge_rubric on all 20 rows
tasks.jsonl: fix stale web port and enrich two rubrics @DEM1TASSE (cherry-picked from #59) reviewer's port fix + rubric enrichment
chore(healthline): integrate as site 25 — tasks on port 40024, docs sweep, registry tests @JeremyJC67 web → 40024 on all 20 rows; README / AGENTS / CONTRIBUTING / CLAUDE / agent_demo README to 25 sites and 40000-40024; walmart_careers + rotten_tomatoes registry tests bumped the same way #95 does

Site

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.comTestPass123!), 18 saved articles, 16 history rows. Everything an HTTP handler reads comes from instance_seed/healthline.db; seeding is gated at function level so /reset/healthline is 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 in tasks.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

Verification (full 25-site environment, fresh clone on a Linux host, 2026-09-11)

check result
python3 -m py_compile app / seed / 21 verifier files clean
./scripts/check_assets.sh all 25 sites have instance_seed/
docker build ok, image size 4.71 GB
HTTP sweep on alt ports 43000-43024 25/25 return 200
GET /health ok: true, 25/25 alive and ready; healthline alive on 40024
POST /reset/healthline → md5 instance vs instance_seed a8a9b54338a0897c01aff11aa4a61ef5 == a8a9b54338a0897c01aff11aa4a61ef5
dirty request then reset md5 unchanged (a8a9b54…)
POST /reset-all ok, 25/25 ready, 1.71 s wall
docker restart then md5 still a8a9b54… == seed
merriam_webster still on index 15, healthline on 24 <title>Merriam-Webster … on 43015, <title>Healthline … on 43024
verifier no-op matrix (homepage only, empty answer, clean DB) 20/20 FAIL, 0 false positives (--no_llm True, DBs pulled from the container)
walmart_careers / rotten_tomatoes registry + docs tests 4 passed (registry / docs-range / manifest selections, 3,343 subtests); the immutable-pin assertion is red by design until HF #70 merges, as it was for #95

Tarball on HF #70: 12,393,920 bytes, sha256 e0a212efecae1cc93882d8692889ecc8d5642804032a54c1a6622aec02f6081b, 162 members, 0 AppleDouble entries, passes scripts/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

@MufanQiu

MufanQiu commented Jul 1, 2026

Copy link
Copy Markdown

Review — healthline (PR #53)

Verdict: REQUEST CHANGES.

We deployed the environment locally and ran the full review checklist; every finding below was independently reproduced, and fidelity was checked against the real upstream site.

Issues (summary)

  • BLOCKER: Port 40015 collides with released merriam_webster; PR's stale base deletes merriam_webster
  • MINOR: .assets-revision pinned to unmerged HF PR ref refs/pr/36 (CI fail-closed)
  • MINOR: HF asset tarball ships 162 AppleDouble ._* macOS junk files
  • NIT: websyn_start.sh site-count comments are internally inconsistent (12/16/16/15)
  • NIT: Repo-root .gitignore scraped_data pattern defeated by trailing inline comment
  • NIT: A few browse tasks (notably T1) expose the answer title at the filtered-list level

Mechanical checks: FAIL

Two BLOCKERS, both confirmed against the repo. F1 — .assets-revision pinned to an unmerged HF PR ref (CI fail-closed). The PR's wt/healthline/.assets-revision reads revision: refs/pr/36, with a comment admitting it is a temporary pin to HuggingFace discussions/36 to be "bump[ed] to the merge commit SHA (or back to main)" once the paired HF dataset PR merges. The second blocker (F2) — the port-40015 collision with the released merriam_webster — is detailed in the issue summary above.

Visual fidelity: PASS

Faithful Healthline clone across 13 pages. Green health|line wordmark, hero feature, section strips (Health Conditions / Nutrition / Mental Health / Fitness / Wellness) with real thumbnails, reviewer bylines, and read-times.

Functional depth: PASS

All flows drive correctly via Playwright with CSRF tokens on every form. Login (alice.j@test.com) → /account renders "Alice Johnson".

Task quality: PASS (with nits)

20 tasks, all with verified DB ground truth against a well-stocked seed (67 articles, 52 conditions, 52 drugs, 22 reviewers, 4 benchmark users).

Required fixes before approval

  1. (BLOCKER, F1) Merge HF dataset PR Add Bandcamp WebHarbor mirror #36 into ChilleD/WebHarbor, then change .assets-revision from revision: refs/pr/36 to the resulting merged commit SHA on main that contains healthline.tar.gz (or main once merged).
  2. (BLOCKER, F2 + MAJOR) Rebase onto current main (which ships merriam_webster at port 40015) and append healthline as a new site at index 16 → port 40016.
  3. (MINOR, F3) Strip AppleDouble ._* files (162 ship into the image) before packing healthline.tar.gz.
  4. (MINOR, F4) Reconcile the websyn_start.sh site-count comments (lines 2, 20, 54, 81) to the true count after the port-slot fix; lines 2 ("12") and 81 ("15") are currently wrong and contradictory.
  5. (NIT, F5) In the repo-root .gitignore, move the inline comment off the sites/*/scraped_data/ line onto its own preceding line so the pattern is unambiguous (currently masked locally by sites/healthline/.gitignore).

JeremyJC67 added a commit to JeremyJC67/WebHarbor that referenced this pull request Jul 2, 2026
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>
@JeremyJC67
JeremyJC67 force-pushed the add-healthline-site branch from 74d02ea to 083a379 Compare July 2, 2026 07:19
@JeremyJC67

Copy link
Copy Markdown
Author

Thanks for the thorough review — all points addressed. Rebased onto current main; summary below.

F2 (BLOCKER — port collision / stale base): Rebased onto latest main. Healthline is now registered as a new site at index 16 → port 40016, appended after merriam_webster in websyn_start.sh + control_server.py, and Dockerfile EXPOSE bumped to 40000-40016. merriam_webster is fully preserved (diff only appends; it is not deleted). Verified locally: 17/17 sites return HTTP 200, :40015 serves Merriam-Webster, :40016 serves Healthline.

F3 (AppleDouble junk): healthline.tar.gz repacked with COPYFILE_DISABLE=1 and ._* / .DS_Store excluded — 0 junk entries (162 total: 160 images + DB + dir). Re-uploaded as a fresh HF asset PR based on current main (so it contains all 17 tarballs and merges cleanly); the old HF PR #36 is closed as superseded.

F1 (.assets-revision unmerged ref): The new HF asset PR is clean and based on current main. .assets-revision now points at it (refs/pr/40) so this PR builds end-to-end. Could you merge the HF asset PR (https://huggingface.co/datasets/ChilleD/WebHarbor/discussions/40)? Once merged I'll immediately repin .assets-revision to the merged commit SHA (happy to do it, or you can).

F4 (site-count comments): Reconciled — all counts in websyn_start.sh now read 17 consistently.

F5 (root .gitignore): No longer applies on current main (the sites/*/scraped_data/ comment is already on its own line); resolved by the rebase.

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 (a8a9b54… matches on /reset/healthline), scored search returns ≥6 (omega-3 → 7, migraine → 7), task answers appear only on detail pages, and the 4 benchmark users + saved/history rows are intact.

@DEM1TASSE

Copy link
Copy Markdown

Reviewer verifiers → submitted as #59

The deterministic verifiers + judge rubrics for this site are up as #59, which bundles
your Healthline site commits (authorship preserved) with my verifier commit so both of us
stay credited. Because your PR is already rebased onto main at port 40016, #59 is
mergeable with no conflict. Full review below.


Review — healthline (PR #53) — verifier deliverable + re-verification

Reviewer: DEM1TASSE · Site author: @JeremyJC67 · Date: 2026-07-04
Verdict: APPROVE. MufanQiu's earlier review (REQUEST CHANGES) has been fully addressed by the
author; I independently re-verified the fixes and am adding the missing verifier + rubric
deliverable (the earlier review did not include verifiers).

Reviewed by building the image and driving a real Chromium (Playwright) against the mirror on
alt ports (control plane :8202, sites :41000-41016); assets fetched from HF discussion #40.


1. Mechanical checks — PASS (re-verified the addressed blockers)

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=lisinopril card 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 hiding drug_class from 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

  1. Merge HF discussion Add AKC mirror site #40, then repin .assets-revision to 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.

JeremyJC67 added a commit to JeremyJC67/WebHarbor that referenced this pull request Jul 4, 2026
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>
@JeremyJC67

Copy link
Copy Markdown
Author

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 cde5031 to this branch: the search result card now shows the broader category (e.g. "Heart Medications") instead of drug_class, so T16 ("which is the ACE inhibitor / which is the statin") can no longer be answered from the results page — the ACE-vs-statin distinction now requires opening each drug's detail page. drug_class is unchanged on the drug detail page, and the search backend still matches on it. Verified on a fresh build: /search?q=lisinopril no longer renders "ACE inhibitor" (shows "Heart Medications"), lisinopril is still findable, the detail page still shows the class, and search breadth is unchanged (omega-3 → 7, migraine → 7).

Since #59 is the merge vehicle, could you fold cde5031 into it (rebase/cherry-pick onto the updated add-healthline-site)? It's a one-line template change ({{ d.drug_class }}{{ d.category }} in search.html). Happy to open it as a patch against your branch instead if that's easier.

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 .assets-revision from refs/pr/40 to the merged commit SHA. Just ping me when it lands.

Thanks again!

@DEM1TASSE

Copy link
Copy Markdown

Follow-up review — healthline (PR #53), additional findings

Thanks for the quick turnaround on the drug_class search leak (cde5031) — swapping the search
card to show category ("Heart Medications") instead of drug_class is exactly right, and it
restores T16 to a genuine two-detail-page compare. I'll fold cde5031 into #59 so the merge vehicle
carries it (one-line search.html change).

On a closer task-by-task walk-through after the approval, I found a few more items. None block
grading
— the verifiers in #59 gate on the /condition/<slug> and /drug/<slug> detail pages, so
scoring is unaffected — but they affect navigation fidelity and one task's solvability, and they're
worth fixing before merge.

1. Two redundant "drugs" nav entries — and the descriptive one is a dead page

The nav has two drug entries, and they're confusingly split:

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.

DEM1TASSE pushed a commit to DEM1TASSE/WebHarbor that referenced this pull request Jul 4, 2026
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>
JeremyJC67 added a commit to JeremyJC67/WebHarbor that referenced this pull request Jul 4, 2026
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>
@JeremyJC67

Copy link
Copy Markdown
Author

Thanks for the detailed second pass — these are great catches. Pushed 1194db0 addressing all four author-side items:

#1 — dead "Drugs & Medications" nav → real list. The "Drugs & Medications" nav item now links to /drugs (the working list) instead of the empty /section/drugs, and I removed the now-redundant "Drugs A–Z" item. So there's one working, correctly-labeled drugs entry that matches the task wording ("open the Drugs & Medications section"). Same treatment in the footer.

#2 — Conditions nav mismatch. The "Health Conditions" nav item now links to /conditions (the condition A–Z pages) instead of /section/health-conditions, and I removed the redundant "A–Z Conditions" item. So the real condition pages (Type 2 Diabetes, hypertension, migraine) now sit under the "Health Conditions" nav item the T3/T17 wording points to. Verified via Playwright: nav "Health Conditions" → /conditions/condition/type-2-diabetes reachable.

#4 — T14 solvability. /history rows now show the section name, e.g. "Health Conditions · Respiratory · viewed May 14, 2026" (added a section_names map to the global context). The section (T14's answer) is now readable directly on the history page. Confirmed on bob's history — all rows show "Health Conditions".

#5 — register "Full name". Now required in both the form (required) and the route validation.

#3tasks.jsonl port 40015 → 40016. Thanks for offering to handle this in #59 — that's my rebase miss. Please go ahead there since you're already editing tasks.jsonl; happy to fix on my side instead if you'd rather.

Re-verified after the changes: 17/17 sites return 200, byte-identical reset still holds (a8a9b54…), and Merriam-Webster is unaffected at 40015.

Branch head is now 1194db0 (adds these four fixes on top of cde5031). Whenever you fold my commits into #59, that's the one to take. And I'll repin .assets-revision the moment HF discussion #40 is merged — just ping me.

@JeremyJC67

Copy link
Copy Markdown
Author

Gentle nudge — anything still needed from my side here? To recap, all three review rounds are addressed and the branch (1194db0) is mergeable/clean. The only open handoffs are (1) merging HF asset PR #40 — I'll repin .assets-revision to the merged SHA the moment it lands — and (2) folding my commits into #59 (plus the tasks.jsonl 40015→40016 port fix you offered to take there). Happy to help however's easiest. Thanks!

JeremyJC67 and others added 6 commits September 11, 2026 14:03
…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>
@JeremyJC67 JeremyJC67 changed the title feat: add Healthline site mirror (20 tasks, hardened) feat(healthline): add Healthline mirror + task verifiers (site 25, port 40024) Sep 11, 2026
@JeremyJC67

Copy link
Copy Markdown
Author

Rebased onto current main — PR is mergeable again and now self-contained

@Raibows @DEM1TASSE @MufanQiu — since the last round main grew from 17 to 24 sites, so both this PR and the reviewer's #59 went CONFLICTING. I have:

  1. Rebased the site onto current main as site 25 / index 24 / port 40024 (merriam_webster stays on 40015; compass 40022; walmart_careers 40023). Registration in websyn_start.sh / control_server.py / Dockerfile, docs swept to 25 sites exactly as feat(webmd_doctor): add WebMD Doctor mirror (site 25, port 40024) #95 does, walmart_careers + rotten_tomatoes registry tests bumped the same way.
  2. Folded the grading contract from Add Healthline mirror + task verifiers (site by @JeremyJC67, verifiers by reviewer) #59 into this PR by cherry-picking @DEM1TASSE's two commits (verifiers + rubrics, and the tasks.jsonl port fix) with their authorship preserved, so one PR carries site + 20 verifiers + rubrics. web on all 20 rows is 40024. No site behaviour changed since the round-3 approval.
  3. Re-opened the asset PR on current asset main: HF Add Kaggle mirror + task verifiers (site by @KaKituken, verifiers by reviewer) #70 (same bytes as Add AKC mirror site #40: sha256 e0a212ef…, seed md5 a8a9b54…, 0 AppleDouble). .assets-revision is pinned to refs/pr/70 temporarily (strict superset of asset main), to be replaced by the merge sha once Add Kaggle mirror + task verifiers (site by @KaKituken, verifiers by reviewer) #70 lands — 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. HF Add AKC mirror site #40 is closed as superseded.

Fresh-clone verification on a Linux host (full 25-site image): 25/25 sites 200, /health 25/25, byte-identical reset before and after a dirty write and after docker restart (md5 a8a9b54…), reset-all 25/25 in 1.71 s, no-op verifier matrix 20/20 FAIL, registry tests green. Full table in the PR description.

Two things only a maintainer can do: merge HF #70 (then I repin within the hour), and pick the slot if #95 (webmd_doctor, also index 24) lands first — a one-line bump, happy to do it immediately.

JeremyJC67 and others added 4 commits September 11, 2026 18:17
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
JeremyJC67 and others added 2 commits September 11, 2026 18:18
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
@JeremyJC67

Copy link
Copy Markdown
Author

Second-pass audit changelog

Ran the maintainer merge-blocker checklist against sites/healthline end to end and fixed
everything it turned up. 20 findings: 2 blocker, 8 major, 7 minor, 3 nit. 19 fixed on this
branch, 1 report-only (needs a Hugging Face re-upload).

No seed bytes changed. instance_seed/healthline.db is still a8a9b54338a0897c01aff11aa4a61ef5,
seed_data.py is untouched, and no ground truth or verifier expectation moved.

Six commits on top of f7d8925:

commit scope
a784a50 pin runtime dependencies to the image versions
6ace057 WCAG AA interactive colour, focus indicator, narrow-width layout
fb465a5 branded 404 / 500 / 413 pages and a favicon
ecfdf2f app hardening, read-only reads, neutral search ordering, POST logout
015dfb3 verifier evidence binding and read-only state enforcement
ad476bb task 14 wording

Blockers

# finding before after
B1 SECRET_KEY was a literal in git, so a session cookie could be forged signing {"_user_id":"1"} with the in-repo key and requesting /account returned 200 rendering alice.j@test.com 302; key is now HEALTHLINE_SECRET_KEY or secrets.token_hex(32), matching the six most recently merged sites
B2 GET /article/<slug> incremented view_count and committed on every anonymous read DB md5 72ac5b52... -> 3d9eb0c4... on a pure read boot + 3 anonymous GETs leave the DB byte-identical to the seed

B2 also blocked the 15 read-only tasks from asserting tables_unchanged and made every
view_count-ordered surface depend on the agent's own browsing. The authenticated reading-history
write stays — it is a real user action and it is what task 14 reads.

Major

# finding before after
M1 header could not shrink; horizontal overflow on every route at 390px and 320px scrollWidth 625 vs 390/320, 12/12 routes "overflow": [] at 1440 / 768 / 390 / 320
M2 all 20 verifiers raised a traceback on malformed input FileNotFoundError for a missing run_dir; same for a missing --after_db on 7/10/18 structured {task_id, pass:false, reason, evidence} FAIL, plus a sys.excepthook catch-all
M3 no evidence binding a run with an empty screenshots/ passed 20/20 screenshots must be decodable PNGs >=512B and >=200x200; task_id must match; step URLs must be loopback
M4 read-only tasks never checked state a dirty after-DB passed verify_0/3/12/17 tables_unchanged enforced for every task that never resolves the after-state DB; the same DB now fails 0/3/6/12/14/17
M5 MAX_CONTENT_LENGTH unset 60 MB body accepted 256 KB cap; 60 MB and 300 KB return 413
M6 /register accepted unbounded and malformed input 10 000-char username, not-an-email, and a 2-char password all returned 302 /account all three return 400
M7 ?page=<int64 overflow> 500 200
M8 /logout reachable by GET and HEAD GET 302, POST 405 GET/HEAD 405, POST 302 with CSRF-protected forms

Minor

  • White on the brand teal #1EA896 is 2.959:1 — below AA and below even the 3:1 large-text
    floor. Pixel-sampled from a rendered button, not read from the stylesheet. Interactive surfaces
    moved to a new --hl-action #168578 token (4.506:1); the brand teal is now decoration
    only. .evidence-badge and .flash.success were 4.072:1 on #e6f7f4 and moved to #0f6e63.
  • .form-row input:focus set outline: none, leaving a 2.959:1 border as the only focus
    affordance. Replaced with a 3px :focus-visible ring.
  • No 404/500 templates or errorhandlers — every unknown route returned the bare 207-byte Werkzeug
    page. 17 of 24 merged sites ship a 404 and 15 ship a 500.
  • Two open redirects: login?next= and toggle_save's request.referrer both followed
    https://evil.example/pwn. Both now go through safe_redirect_target.
  • /search collected conditions and drugs with no ORDER BY, returning seed-authoring order.
    Both now ORDER BY name, matching the A-Z pages. No limit is applied, so the re-order cannot
    truncate a target.
  • verify_11 accepted a bare /drugs visit and verify_13 a bare /search visit. Both answers
    (sertraline's 50 mg 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 and a
    truncated trajectory passed. Both now require the detail page.
  • number_mentioned used a bare substring, so "15 saved articles" satisfied an expected 5 and
    "1600 IU" an expected 600. Now anchored on both sides.

Nits

  • requirements.txt was unpinned and omitted SQLAlchemy, which app.py imports directly.
  • No favicon; /favicon.ico returned 404.

Verifier state after the pass

baseline (correct runs)            20/20 pass
contradictory answer               fails everywhere except 7/10/18 (by design: pure state tasks)
answer-only, cross-task, truncated all 20 fail
tiny/absent screenshots            all 20 fail   (was: all 20 PASS)
off-origin step URLs               all 20 fail
wrong task_id                      all 20 fail
read-only task that wrote to DB    0/3/6/12/14/17 fail   (was: pass)
malformed input                    structured FAIL, no traceback

Report-only

74 of the 160 images in healthline.tar.gz (7.2 MB of 12.6 MB, 57%) are referenced by no seed
row. Pruning them needs a repack and re-upload to HF discussion #70, which the audit branch cannot
do. The site is fully functional without it: 0 referenced-but-missing images, 0 broken image
references in the seed DB, and broken_img: [] across 12 routes x 4 widths. The only cost is
image-layer weight. The exact repack command and the file list are recorded for the maintainer.

.assets-revision still points at refs/pr/70 and must be re-pinned to the merged commit sha
once that HF discussion lands — known, disclosed, and already documented in the file.

Things checked that turned out to be fine

  • Form prefill is clean on /register and /login; /account/edit prefills the user's own
    profile, which is the documented exception.
  • Only two attribute strings exist site-wide (aria-label="Search" and the search placeholder);
    no homepage link carries a prefilled q=; /conditions leaks neither "autoimmune" (task 12)
    nor "silent killer" (task 17).
  • CSRF is enforced on all five POST routes with zero @csrf.exempt.
  • Distractors are healthy and no target sits at rank 1: vitamin-d-101 rank 3 of 14, omega-3-guide
    3 of 6, mediterranean-diet 2 of 3, sertraline 7 of 9, hypertension 3 of 6, lisinopril 7 of 11.
  • drug_class on the Drugs A-Z cards shows task 16's answer, but both candidates appear together
    on a comprehensive index, so the anti-shortcut exception applies and the verifier still requires
    both detail pages. Left as is.
  • One reported symptom was a false alarm from the audit's own tooling: a "console 404 on every
    page" turned out to be /author/kim-chin, a URL the probe invented (the real key is
    kim_chin, which returns 200). Not a site defect.

Not verified here

Docker was not run, per the audit's operating rules. Container-level checks — build-time seed
byte-reproducibility, the 25-site reset-all matrix, and the cold-checkout chain — passed on the
previous round at f7d8925 and this branch changes no seed-producing code. The exact commands to
re-run them are recorded for the maintainer. Verifier runs used --no_llm, so the LLM
answer_consistent checks were skipped; the deterministic checks, which are the primary grader,
were exercised in full. Trajectories were synthetic, built to the recorder's exact schema.

Container re-verification of this branch (ad476bb, full 25-site image)

Re-ran the whole environment after the fixes, on the same Linux host as the previous round.

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.

@JeremyJC67

Copy link
Copy Markdown
Author

Tooling disclosure

Added 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
adjudication calls (does this string leak an answer, is this task wording ambiguous) and as a second
opinion on our own checks. It produced no verdicts: every finding in the comment above is backed by a
command and its output, and the shipped verifiers decide with --no_llm, so nothing in the grading
contract depends on a model.

Where it changed what we did on this PR: it adjudicated the missing ORDER BY on /search as a real
defect, which we then fixed; and it ruled that drug_class on the Drugs A-Z cards is not a leak,
because every candidate of that question appears together on a comprehensive index — so we left it alone
instead of "fixing" a non-problem, and the verifier still requires both detail pages.

No call log was kept, so I cannot enumerate every consultation — that is the honest limit of this
disclosure.

JeremyJC67 and others added 2 commits September 12, 2026 21:14
…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
@JeremyJC67

Copy link
Copy Markdown
Author

Rebased onto the 26-site main — this branch is now site 27 / port 40026

main merged FedEx (40024) and WebMD Doctor (40025) overnight, which put every open site
branch into conflict. This branch has been brought up to date by merging main rather than
rebasing, so every contributor and reviewer commit is preserved verbatim, and re-slotted to
index 26 / port 40026: registry, Dockerfile EXPOSE, the shared docs and every
site-local port reference follow. The slot is provisional as always — say the word at merge
time and I will re-slot in whatever order you land things.

The whole environment was then rebuilt and re-verified at the new slot:

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.

@JeremyJC67

Copy link
Copy Markdown
Author

@Raibows a consolidated status on the Healthline line and on five reviews, now that everything is
rebuilt against the 26-site main. All six branches below merged main (merged rather than
rebased, so contributor commits are preserved verbatim), re-slotted to index 26 / port 40026, and
were re-verified on a full 27-site container build.

Healthline — #53 is the current vehicle. @DEM1TASSE's two verifier commits are cherry-picked in
with their authorship preserved. Container run: 27/27 sites 200, /health 27/27, POST /reset/healthline byte-identical (a8a9b54338a0897c01aff11aa4a61ef5) before and after a dirty
request and after docker restart, reset-all 27/27 in 1.52 s, verifier no-op matrix 20/20 FAIL.
#59 is its stale predecessor; @TabsPhasers has since opened #105 from that older base, and I have
offered there either to fold my work into theirs or to take their three verifier predicates into
#53 — whichever you prefer as the merge vehicle. The only open item is merging
HF #70, after which I repin
.assets-revision the same day. HF #40 is closed and superseded by #70.

Track B — five environments reviewed end to end, each as a Review: PR carrying the
contributor's commits plus a deterministic verifier and judge rubric for every task, a validation
matrix (no-op / genuine / shortcut / wrong-answer / state-mismatch) and a full container check:

PR site contributor what is left
#96 4shared @sarendis56 merge HF #62, then repin
#97 9GAG @sarendis56 merge HF #65, then repin
#98 AccuWeather @sarendis56 merge HF #66, then repin
#99 Amtrak @Lxr-max no ChilleD/WebHarbor revision contains amtrak.tar.gz at all; needs an HF PR first
#100 Adopt-a-Pet @sarendis56 merge HF #67, then repin

All six are MERGEABLE/CLEAN as of this comment. @sarendis56 has already repacked #65, #66 and #67
in response to the reviews; I downloaded each and confirmed with the repo's own
validate_asset_archive.py (44 / 10 / 34 managed members, correct roots, no AppleDouble), and
their Adopt-a-Pet photo remediation is merged into #100.

A second pass against the merge-blocker checklist then found and fixed a recurring class of defect
across the six sites. Two are worth your attention rather than a reviewer's: a hardcoded
SECRET_KEY lets a forged cookie read /account as a seeded user, and it is present on most
already-merged sites too; and read-only routes committing to the DB, which breaks reset
byte-identity (found on Healthline's view_count and Amtrak's search_logs).

One repo-wide item. .dockerignore excludes sites/*/verify/tests/ but not
sites/*/verify/, so each site's ground_truth.py — the full answer key — is COPYd into the
published image. Not reachable over HTTP, but it ships inside the artifact for every site with a
verify/ directory. Happy to send a one-line patch if you want it excluded.

Port slot. All six register as index 26 / 40026 because they were prepared against the same
main. Tell me the order you want to land them and I will re-slot mine accordingly.

@TabsPhasers

Copy link
Copy Markdown

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 TabsPhasers/WebHarbor:review/pr-59-healthline @ cdf5865; the request is to cherry-pick our 8 verifier-predicate deltas (listed on #105) with authorship preserved. No action needed from #53's side until then.

TabsPhasers and others added 2 commits September 12, 2026 22:33
…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
@JeremyJC67

Copy link
Copy Markdown
Author

@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
cdf5865: your tree predates the evidence-binding and read-only-state work on this branch, so
taking your verify_lib wholesale would have removed it. The commit is 247098b, authored to you.

# delta where
1 count_groups, overlapping symptom tokens count once verify_lib, verify_3
2 amount_with_unit, a standalone 50 mg with a unit verify_lib, verify_11
3 classes_not_reversed, reject swapping the two drug classes verify_16
4 fail closed when the initial DB is unavailable verify_6
5 require the reviewer's name, not just the credential verify_8
6 broadened affirm/negate pair verify_15
7 require the condition identity verify_17
8 same grouping fix for migraine triggers verify_19

number_mentioned was not taken, per your note that this branch's anchoring is equivalent.

Assets. HF #70 merged, so .assets-revision now pins the immutable merge commit
805e6938232f4fbf7447688faf91c9c02aca3b75 instead of refs/pr/70 (6f47188). That revision
carries all 29 tarballs, so a cold build has everything the 27-site registry needs.

Verified after both changes, on a full 27-site container build with the assets fetched from the
merged pin rather than a PR ref — which is what makes the repin real rather than asserted:

fetch_assets.sh healthline @ 805e6938...   validated 162 managed members
HTTP sweep                                  27/27 return 200
/health                                     ok, 27/27 alive and ready
POST /reset/healthline                      a8a9b54338a0897c01aff11aa4a61ef5 == seed
docker restart, then md5                    unchanged
POST /reset-all                             27/27 ready, 1.61 s
verifier no-op matrix                       20/20 FAIL, 0 errors
registry + docs tests                       4 passed, 3,343 subtests
GET /logout 405 · 300 KB POST 413 · branded 404 (4,051 bytes) · read-only browsing byte-identical

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,
verifiers by @DEM1TASSE, the eight predicate refinements by @TabsPhasers, assets merged and pinned
to an immutable revision, and the whole environment green at the new slot. The only open question
left is the port slot itself — this and the five review PRs all register as index 26 / 40026
because they were prepared against the same main, so tell me the order you want to land them and
I will re-slot accordingly.

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.

4 participants