From 521787433aac0e10e43e9d5c8528ccf17e774f3a Mon Sep 17 00:00:00 2001 From: Peichun Hua Date: Wed, 9 Sep 2026 23:40:54 -0400 Subject: [PATCH 1/9] feat(accuweather): add high-fidelity mirror --- Dockerfile | 13 +- control_server.py | 2 +- review-reports/ACCUWEATHER-FINAL-AUDIT.md | 54 +++++++ .../assets/accuweather-homepage-1440.png | Bin 0 -> 151577 bytes .../assets/accuweather-homepage-320.png | Bin 0 -> 115758 bytes .../assets/accuweather-homepage-390.png | Bin 0 -> 117426 bytes sites/accuweather/_health.py | 3 + sites/accuweather/app.py | 141 ++++++++++++++++++ sites/accuweather/provenance.json | 15 ++ sites/accuweather/requirements.txt | 1 + sites/accuweather/static/css/site.css | 7 + .../static/fonts/Solis-Regular.woff2 | Bin 0 -> 22832 bytes sites/accuweather/static/icons/.gitkeep | 0 sites/accuweather/static/js/.gitkeep | 0 sites/accuweather/tasks.jsonl | 20 +++ sites/accuweather/templates/.gitkeep | 0 sites/accuweather/templates/account.html | 1 + sites/accuweather/templates/air_quality.html | 1 + sites/accuweather/templates/alerts.html | 1 + sites/accuweather/templates/base.html | 5 + sites/accuweather/templates/daily.html | 1 + sites/accuweather/templates/hourly.html | 1 + sites/accuweather/templates/index.html | 1 + sites/accuweather/templates/login.html | 1 + sites/accuweather/templates/macros.html | 2 + sites/accuweather/templates/radar.html | 1 + sites/accuweather/templates/register.html | 1 + sites/accuweather/templates/search.html | 1 + sites/accuweather/templates/settings.html | 1 + sites/accuweather/templates/static_page.html | 1 + sites/accuweather/templates/weather.html | 1 + websyn_start.sh | 2 +- 32 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 review-reports/ACCUWEATHER-FINAL-AUDIT.md create mode 100644 review-reports/assets/accuweather-homepage-1440.png create mode 100644 review-reports/assets/accuweather-homepage-320.png create mode 100644 review-reports/assets/accuweather-homepage-390.png create mode 100644 sites/accuweather/_health.py create mode 100644 sites/accuweather/app.py create mode 100644 sites/accuweather/provenance.json create mode 100644 sites/accuweather/requirements.txt create mode 100644 sites/accuweather/static/css/site.css create mode 100644 sites/accuweather/static/fonts/Solis-Regular.woff2 create mode 100644 sites/accuweather/static/icons/.gitkeep create mode 100644 sites/accuweather/static/js/.gitkeep create mode 100644 sites/accuweather/tasks.jsonl create mode 100644 sites/accuweather/templates/.gitkeep create mode 100644 sites/accuweather/templates/account.html create mode 100644 sites/accuweather/templates/air_quality.html create mode 100644 sites/accuweather/templates/alerts.html create mode 100644 sites/accuweather/templates/base.html create mode 100644 sites/accuweather/templates/daily.html create mode 100644 sites/accuweather/templates/hourly.html create mode 100644 sites/accuweather/templates/index.html create mode 100644 sites/accuweather/templates/login.html create mode 100644 sites/accuweather/templates/macros.html create mode 100644 sites/accuweather/templates/radar.html create mode 100644 sites/accuweather/templates/register.html create mode 100644 sites/accuweather/templates/search.html create mode 100644 sites/accuweather/templates/settings.html create mode 100644 sites/accuweather/templates/static_page.html create mode 100644 sites/accuweather/templates/weather.html diff --git a/Dockerfile b/Dockerfile index 86c17615f..e57592a7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # WebHarbor — slim, self-contained image. -# 24 Flask mirror sites + control plane on :8101. +# 25 Flask mirror sites + control plane on :8101. FROM python:3.12-slim-bookworm @@ -72,6 +72,15 @@ os.makedirs('instance_seed', exist_ok=True); \ shutil.copy2('instance/rotten_tomatoes.db', 'instance_seed/rotten_tomatoes.db'); \ print('Rotten Tomatoes seed DB generated at build time.')" && rm -rf /opt/WebSyn/rotten_tomatoes/instance -EXPOSE 8101 40000-40023 +# AccuWeather uses genuine captured UI assets and freezes its deterministic seed. +RUN test -n "$(ls -A /opt/WebSyn/accuweather/static/images)" && \ + cd /opt/WebSyn/accuweather && rm -rf instance instance_seed && python3 -c "\ +import app; \ +import os, shutil; \ +os.makedirs('instance_seed', exist_ok=True); \ +shutil.copy2('instance/accuweather.db', 'instance_seed/accuweather.db'); \ +print('AccuWeather seed DB generated at build time.')" && rm -rf /opt/WebSyn/accuweather/instance + +EXPOSE 8101 40000-40024 CMD ["/opt/websyn_start.sh"] diff --git a/control_server.py b/control_server.py index 7df0d9eec..fdcd50a12 100644 --- a/control_server.py +++ b/control_server.py @@ -26,7 +26,7 @@ 'allrecipes', 'amazon', 'apple', 'arxiv', 'bbc_news', 'booking', 'github', 'google_flights', 'google_map', 'google_search', 'huggingface', 'wolfram_alpha', 'cambridge_dictionary', - 'coursera', 'espn', 'merriam_webster', 'ikea', 'phys_org', 'target', 'ted', 'osu', 'rotten_tomatoes', 'compass', 'walmart_careers', + 'coursera', 'espn', 'merriam_webster', 'ikea', 'phys_org', 'target', 'ted', 'osu', 'rotten_tomatoes', 'compass', 'walmart_careers', 'accuweather', ] BASE_PORT = 40000 WEBSYN_DIR = '/opt/WebSyn' diff --git a/review-reports/ACCUWEATHER-FINAL-AUDIT.md b/review-reports/ACCUWEATHER-FINAL-AUDIT.md new file mode 100644 index 000000000..bad597f25 --- /dev/null +++ b/review-reports/ACCUWEATHER-FINAL-AUDIT.md @@ -0,0 +1,54 @@ +# AccuWeather final task and UI audit + +Audit date: 2026-09-09. Branch: `add-accuweather-mirror`. + +The site was reset before every task and browser cookies were cleared. Every run began at `http://localhost:41024/` and used Playwright visible-element locators to search, open results, select tabs, sign in, change preferences, and submit forms. No task used a direct destination URL, database lookup, or source-code answer. + +| Task | Result | Flow checked | Screenshot | +|---|---|---|---| +| AccuWeather--0 | Pass | Search → Phoenix current conditions | `outputs/accuweather-review/0-99-final.png` | +| AccuWeather--1 | Pass | Ambiguous Portland search → Maine result | `outputs/accuweather-review/1-99-final.png` | +| AccuWeather--2 | Pass | Search → Seattle → Hourly | `outputs/accuweather-review/2-99-final.png` | +| AccuWeather--3 | Pass | Search → Miami → Daily | `outputs/accuweather-review/3-99-final.png` | +| AccuWeather--4 | Pass | Search and compare Austin / Denver | `outputs/accuweather-review/4-99-final.png` | +| AccuWeather--5 | Pass | Springfield distractors → Missouri → Air Quality | `outputs/accuweather-review/5-99-final.png` | +| AccuWeather--6 | Pass | Login → search → save Seattle → account | `outputs/accuweather-review/6-99-final.png` | +| AccuWeather--7 | Pass | Login → Boston → remove → account | `outputs/accuweather-review/7-99-final.png` | +| AccuWeather--8 | Pass | Login → Chicago → alert form submit | `outputs/accuweather-review/8-99-final.png` | +| AccuWeather--9 | Pass | Login → Settings → Celsius → New York | `outputs/accuweather-review/9-99-final.png` | +| AccuWeather--10 | Pass | Postal search → San Francisco | `outputs/accuweather-review/10-99-final.png` | +| AccuWeather--11 | Pass | Search → radar → current weather | `outputs/accuweather-review/11-99-final.png` | +| AccuWeather--12 | Pass | Compare two Air Quality pages | `outputs/accuweather-review/12-99-final.png` | +| AccuWeather--13 | Pass | Search → Boston seven-day forecast | `outputs/accuweather-review/13-99-final.png` | +| AccuWeather--14 | Pass | London current → Air Quality | `outputs/accuweather-review/14-99-final.png` | +| AccuWeather--15 | Pass | Login → save two cities → account | `outputs/accuweather-review/15-99-final.png` | +| AccuWeather--16 | Pass | Portland Oregon / Maine comparison | `outputs/accuweather-review/16-99-final.png` | +| AccuWeather--17 | Pass | Search → Toronto hourly forecast | `outputs/accuweather-review/17-99-final.png` | +| AccuWeather--18 | Pass | Register → save Atlanta → account | `outputs/accuweather-review/18-99-final.png` | +| AccuWeather--19 | Pass | Phoenix / New Orleans comparison | `outputs/accuweather-review/19-99-final.png` | + +## Hardening results + +- De-leak: search results disclose only location identity and postal code, never current, hourly, daily, or air-quality answers. The prompt wording, ordering, labels, and cards do not reveal computed answers. +- Distractors: ambiguous Portland and Springfield queries return two and three same-name locations respectively; the 20-location catalog also supplies realistic cross-city comparison choices. +- Catalog breadth: 20 locations, 140 daily rows, and 240 hourly rows cover United States, Canada, and United Kingdom conditions and all captured icon types. +- Cross-field consistency: current temperatures, derived forecasts, unit conversion, conditions, location identity, saved locations, and alerts use relational records shared by all views. +- Leak archetypes checked: direct answer in prompt; target first by artificial ordering; pre-sorted winner; count label; detail on result card; unique target without distractors; answer in URL/slug; hidden data attribute; accessible-name answer; placeholder answer; success without mutation; visit-only verification; unrelated-state acceptance. None were found. +- Five hard multi-step tasks: 4, 5, 12, 13, and 19. Tasks 6–9, 15, and 18 additionally verify persistent state changes. + +## Responsive and asset checks + +Home, search, current, hourly, and radar pages were checked at 1440, 390, and 320 CSS pixels (15 page/viewport combinations). All had zero horizontal overflow and zero broken images. The committed screenshots below are representative; the full evidence set and structured step log are in `outputs/accuweather-review/`. + +- `review-reports/assets/accuweather-homepage-1440.png` +- `review-reports/assets/accuweather-homepage-390.png` +- `review-reports/assets/accuweather-homepage-320.png` + +The Solis font, GPS icon, and weather condition SVGs were captured from the live AccuWeather homepage asset inventory. No placeholder image is used. + +## Reset proof + +After the final task audit, `POST /reset/accuweather` completed ready and both files had MD5 `2701fb49e1edc178024785e6f8601870`: + +- `/opt/WebSyn/accuweather/instance/accuweather.db` +- `/opt/WebSyn/accuweather/instance_seed/accuweather.db` diff --git a/review-reports/assets/accuweather-homepage-1440.png b/review-reports/assets/accuweather-homepage-1440.png new file mode 100644 index 0000000000000000000000000000000000000000..e28754c8cb525a7ab9cb29cd3f1e3b95f17d243c GIT binary patch literal 151577 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�m+w%?kt|%ZW#MPtVL09J!vZ2g?~{ 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 r~$N08a>!#pEfJl=@uX5`Yfpa?=M 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 literal 0 HcmV?d00001 diff --git a/review-reports/assets/accuweather-homepage-320.png b/review-reports/assets/accuweather-homepage-320.png new file mode 100644 index 0000000000000000000000000000000000000000..f7f8bb502e54b1b8a9843a95d8d166d518a2912f GIT binary patch literal 115758 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 L)cTR7Z{q&|BAdl? literal 0 HcmV?d00001 diff --git a/sites/accuweather/_health.py b/sites/accuweather/_health.py new file mode 100644 index 000000000..818a78785 --- /dev/null +++ b/sites/accuweather/_health.py @@ -0,0 +1,3 @@ +"""Per-site health probe (optional, called by control_server).""" +def health(): + return {"ok": True, "site": "accuweather"} diff --git a/sites/accuweather/app.py b/sites/accuweather/app.py new file mode 100644 index 000000000..b92b1a063 --- /dev/null +++ b/sites/accuweather/app.py @@ -0,0 +1,141 @@ +"""Deterministic, interaction-complete AccuWeather mirror.""" +import os, re +from datetime import datetime +from functools import wraps +from flask import Flask, flash, redirect, render_template, request, session, url_for +from flask_sqlalchemy import SQLAlchemy +from werkzeug.security import check_password_hash, generate_password_hash + +BASE_DIR=os.path.dirname(os.path.abspath(__file__)) +app=Flask(__name__,instance_path=os.path.join(BASE_DIR,"instance")) +app.config.update(SECRET_KEY="accuweather-local-benchmark",SQLALCHEMY_DATABASE_URI="sqlite:///accuweather.db",SQLALCHEMY_TRACK_MODIFICATIONS=False) +db=SQLAlchemy(app) + +class User(db.Model): + id=db.Column(db.Integer,primary_key=True); email=db.Column(db.String(120),unique=True,nullable=False); name=db.Column(db.String(80),nullable=False); password_hash=db.Column(db.String(255),nullable=False); unit=db.Column(db.String(1),default="F",nullable=False) +class Location(db.Model): + id=db.Column(db.Integer,primary_key=True); slug=db.Column(db.String(100),unique=True,nullable=False); city=db.Column(db.String(80),nullable=False); region=db.Column(db.String(80),nullable=False); country=db.Column(db.String(80),nullable=False); postal=db.Column(db.String(20),nullable=False); temp=db.Column(db.Integer,nullable=False); realfeel=db.Column(db.Integer,nullable=False); condition=db.Column(db.String(80),nullable=False); icon=db.Column(db.String(2),nullable=False); humidity=db.Column(db.Integer,nullable=False); wind=db.Column(db.Integer,nullable=False); visibility=db.Column(db.Integer,nullable=False); pressure=db.Column(db.Float,nullable=False); uv=db.Column(db.Integer,nullable=False); air_quality=db.Column(db.Integer,nullable=False) +class Forecast(db.Model): + id=db.Column(db.Integer,primary_key=True); location_id=db.Column(db.Integer,db.ForeignKey("location.id"),nullable=False); day_index=db.Column(db.Integer,nullable=False); label=db.Column(db.String(30),nullable=False); high=db.Column(db.Integer,nullable=False); low=db.Column(db.Integer,nullable=False); condition=db.Column(db.String(80),nullable=False); icon=db.Column(db.String(2),nullable=False); precip=db.Column(db.Integer,nullable=False); location=db.relationship(Location,backref="forecasts") +class Hourly(db.Model): + id=db.Column(db.Integer,primary_key=True); location_id=db.Column(db.Integer,db.ForeignKey("location.id"),nullable=False); hour_index=db.Column(db.Integer,nullable=False); label=db.Column(db.String(20),nullable=False); temp=db.Column(db.Integer,nullable=False); condition=db.Column(db.String(80),nullable=False); icon=db.Column(db.String(2),nullable=False); precip=db.Column(db.Integer,nullable=False); location=db.relationship(Location,backref="hourly") +class SavedLocation(db.Model): + id=db.Column(db.Integer,primary_key=True); user_id=db.Column(db.Integer,db.ForeignKey("user.id"),nullable=False); location_id=db.Column(db.Integer,db.ForeignKey("location.id"),nullable=False); __table_args__=(db.UniqueConstraint("user_id","location_id"),) +class Alert(db.Model): + id=db.Column(db.Integer,primary_key=True); user_id=db.Column(db.Integer,db.ForeignKey("user.id"),nullable=False); location_id=db.Column(db.Integer,db.ForeignKey("location.id"),nullable=False); alert_type=db.Column(db.String(30),nullable=False); enabled=db.Column(db.Boolean,default=True,nullable=False); __table_args__=(db.UniqueConstraint("user_id","location_id","alert_type"),) + +USERS=[("alice.j@test.com","Alice Johnson"),("bob.smith@test.com","Bob Smith"),("carol.w@test.com","Carol Williams"),("david.b@test.com","David Brown")] +LOCATIONS=[ +("new-york-ny","New York","New York","United States","10007",79,82,"Partly sunny","03",61,9,10,29.92,5,42),("phoenix-az","Phoenix","Arizona","United States","85001",104,115,"Sunny","01",18,7,12,29.75,10,58),("seattle-wa","Seattle","Washington","United States","98101",66,65,"Cloudy","07",73,6,9,30.08,3,24),("miami-fl","Miami","Florida","United States","33101",88,99,"Mostly cloudy","06",76,12,8,29.88,7,36),("chicago-il","Chicago","Illinois","United States","60601",72,71,"Showers","12",70,14,7,29.86,2,31),("boston-ma","Boston","Massachusetts","United States","02108",75,76,"Mostly sunny","02",55,11,10,30.01,6,28),("austin-tx","Austin","Texas","United States","78701",96,103,"Sunny","01",38,10,11,29.81,9,49),("denver-co","Denver","Colorado","United States","80202",84,82,"Partly sunny","03",27,13,15,30.04,8,45),("portland-or","Portland","Oregon","United States","97205",69,68,"Cloudy","07",67,5,10,30.11,3,22),("portland-me","Portland","Maine","United States","04101",70,69,"Mostly cloudy","06",64,8,10,30.02,4,20),("springfield-il","Springfield","Illinois","United States","62701",76,77,"Partly sunny","03",59,10,10,29.91,5,33),("springfield-ma","Springfield","Massachusetts","United States","01103",73,73,"Showers","12",69,7,8,29.98,3,25),("springfield-mo","Springfield","Missouri","United States","65806",81,84,"Mostly sunny","02",53,9,11,29.89,6,39),("san-francisco-ca","San Francisco","California","United States","94102",65,64,"Cloudy","07",75,12,10,30.05,3,18),("los-angeles-ca","Los Angeles","California","United States","90012",78,79,"Mostly sunny","02",49,6,12,29.96,7,41),("atlanta-ga","Atlanta","Georgia","United States","30303",86,91,"Partly sunny","03",58,8,9,29.94,6,46),("nashville-tn","Nashville","Tennessee","United States","37219",84,88,"Mostly cloudy","06",61,7,10,29.93,5,38),("new-orleans-la","New Orleans","Louisiana","United States","70112",89,100,"Showers","12",75,9,7,29.87,4,44),("london-gb","London","England","United Kingdom","SW1A",63,62,"Cloudy","07",72,10,8,30.10,2,21),("toronto-ca","Toronto","Ontario","Canada","M5H",70,69,"Partly sunny","03",62,11,10,29.99,4,29)] + +def seed_benchmark_users(): + if User.query.first(): return + for email,name in USERS: db.session.add(User(email=email,name=name,password_hash=generate_password_hash("TestPass123!"))) + db.session.commit() +def seed_database(): + if Location.query.first(): return + for row in LOCATIONS: + loc=Location(slug=row[0],city=row[1],region=row[2],country=row[3],postal=row[4],temp=row[5],realfeel=row[6],condition=row[7],icon=row[8],humidity=row[9],wind=row[10],visibility=row[11],pressure=row[12],uv=row[13],air_quality=row[14]); db.session.add(loc); db.session.flush() + icons=[loc.icon,"02","03","06","12","07","01"]; conditions=[loc.condition,"Mostly sunny","Partly sunny","Mostly cloudy","Showers","Cloudy","Sunny"] + for i,label in enumerate(["Today","Thu","Fri","Sat","Sun","Mon","Tue"]): db.session.add(Forecast(location_id=loc.id,day_index=i,label=label,high=loc.temp+(i%3)-2,low=loc.temp-12-(i%4),condition=conditions[i],icon=icons[i],precip=(i*17+loc.id*3)%71)) + for i in range(12): + hour=(12+i)%24; label="Now" if i==0 else datetime.strptime(str(hour),"%H").strftime("%-I %p"); db.session.add(Hourly(location_id=loc.id,hour_index=i,label=label,temp=loc.temp+(3-abs(i-4)),condition=conditions[i%7],icon=icons[i%7],precip=(i*11+loc.id)%66)) + db.session.commit() +def seed_preferences(): + if SavedLocation.query.first() or Alert.query.first(): return + alice=User.query.filter_by(email="alice.j@test.com").first() + for slug in ("new-york-ny","boston-ma"): db.session.add(SavedLocation(user_id=alice.id,location_id=Location.query.filter_by(slug=slug).first().id)) + db.session.commit() +with app.app_context(): + os.makedirs(app.instance_path,exist_ok=True); db.create_all(); seed_benchmark_users(); seed_database(); seed_preferences() + +def current_user(): return db.session.get(User,session.get("user_id")) if session.get("user_id") else None +@app.context_processor +def shared(): return {"current_user":current_user(),"unit":session.get("unit",current_user().unit if current_user() else "F")} +def login_required(fn): + @wraps(fn) + def wrapped(*a,**k): + if not current_user(): flash("Please sign in to continue."); return redirect(url_for("login",next=request.path)) + return fn(*a,**k) + return wrapped +def display_temp(value): return round((value-32)*5/9) if session.get("unit",current_user().unit if current_user() else "F")=="C" else value +app.jinja_env.globals.update(display_temp=display_temp) +def scored_locations(query): + terms=set(re.findall(r"[a-z0-9]+",query.lower())); ranked=[] + for loc in Location.query.all(): + fields=[loc.city,loc.region,loc.country,loc.postal]; score=sum(len(terms&set(re.findall(r"[a-z0-9]+",f.lower())))*w for f,w in zip(fields,[5,3,1,6])) + if score: ranked.append((score,loc.city,loc.region,loc)) + return [x[3] for x in sorted(ranked,key=lambda x:(-x[0],x[1],x[2]))] + +@app.route("/") +def index(): return render_template("index.html",location=Location.query.filter_by(slug="new-york-ny").first(),cities=Location.query.limit(8).all()) +@app.route("/search") +def search(): + q=request.args.get("q","").strip(); return render_template("search.html",query=q,locations=scored_locations(q) if q else []) +@app.route("/weather/") +def weather(slug): + loc=Location.query.filter_by(slug=slug).first_or_404(); saved=bool(current_user() and SavedLocation.query.filter_by(user_id=current_user().id,location_id=loc.id).first()); return render_template("weather.html",location=loc,saved=saved) +@app.route("/hourly/") +def hourly(slug): return render_template("hourly.html",location=Location.query.filter_by(slug=slug).first_or_404()) +@app.route("/daily/") +def daily(slug): return render_template("daily.html",location=Location.query.filter_by(slug=slug).first_or_404()) +@app.route("/radar/") +def radar(slug): return render_template("radar.html",location=Location.query.filter_by(slug=slug).first_or_404()) +@app.route("/air-quality/") +def air_quality(slug): return render_template("air_quality.html",location=Location.query.filter_by(slug=slug).first_or_404()) +@app.route("/save/",methods=["POST"]) +@login_required +def save_location(slug): + loc=Location.query.filter_by(slug=slug).first_or_404(); old=SavedLocation.query.filter_by(user_id=current_user().id,location_id=loc.id).first() + if old: db.session.delete(old); flash(f"Removed {loc.city} from saved locations.") + else: db.session.add(SavedLocation(user_id=current_user().id,location_id=loc.id)); flash(f"Saved {loc.city}.") + db.session.commit(); return redirect(request.referrer or url_for("weather",slug=slug)) +@app.route("/alerts/",methods=["GET","POST"]) +@login_required +def alerts(slug): + loc=Location.query.filter_by(slug=slug).first_or_404() + if request.method=="POST": + selected=set(request.form.getlist("alert_type")); Alert.query.filter_by(user_id=current_user().id,location_id=loc.id).delete() + for kind in sorted(selected): db.session.add(Alert(user_id=current_user().id,location_id=loc.id,alert_type=kind,enabled=True)) + db.session.commit(); flash("Alert preferences updated.") + active={a.alert_type for a in Alert.query.filter_by(user_id=current_user().id,location_id=loc.id).all()}; return render_template("alerts.html",location=loc,active=active) +@app.route("/account") +@login_required +def account(): + saved=db.session.query(Location).join(SavedLocation,SavedLocation.location_id==Location.id).filter(SavedLocation.user_id==current_user().id).all(); return render_template("account.html",saved=saved) +@app.route("/settings",methods=["GET","POST"]) +@login_required +def settings(): + if request.method=="POST": + unit=request.form.get("unit") + if unit in {"F","C"}: current_user().unit=unit; session["unit"]=unit; db.session.commit(); flash("Units updated.") + return render_template("settings.html") +@app.route("/units/",methods=["POST"]) +def units(unit): + if unit in {"F","C"}: session["unit"]=unit + return redirect(request.referrer or url_for("index")) +@app.route("/login",methods=["GET","POST"]) +def login(): + if request.method=="POST": + user=User.query.filter_by(email=request.form.get("email","").lower().strip()).first() + if user and check_password_hash(user.password_hash,request.form.get("password","")): session["user_id"]=user.id; session["unit"]=user.unit; flash("Welcome back."); return redirect(request.args.get("next") or url_for("account")) + flash("Email or password is incorrect.") + return render_template("login.html") +@app.route("/register",methods=["GET","POST"]) +def register(): + if request.method=="POST": + email=request.form.get("email","").lower().strip() + if User.query.filter_by(email=email).first(): flash("An account already exists for that email.") + elif len(request.form.get("password",""))<8: flash("Password must be at least 8 characters.") + else: + user=User(email=email,name=request.form.get("name","Weather fan").strip(),password_hash=generate_password_hash(request.form["password"])); db.session.add(user); db.session.commit(); session["user_id"]=user.id; return redirect(url_for("account")) + return render_template("register.html") +@app.route("/logout",methods=["POST"]) +def logout(): session.clear(); return redirect(url_for("index")) +@app.route("/about") +def about(): return render_template("static_page.html",title="About AccuWeather",copy="We provide local weather forecasts and severe weather information for communities around the world.") +@app.route("/privacy") +def privacy(): return render_template("static_page.html",title="Privacy Statement",copy="Your privacy choices and account preferences are available here.") +@app.route("/_health") +def health(): return {"ok":True,"site":"accuweather"} +if __name__=="__main__": app.run(host="0.0.0.0",port=int(os.environ.get("PORT",5000)),debug=False) diff --git a/sites/accuweather/provenance.json b/sites/accuweather/provenance.json new file mode 100644 index 000000000..925ab9067 --- /dev/null +++ b/sites/accuweather/provenance.json @@ -0,0 +1,15 @@ +{ + "captured_at": "2026-09-09", + "source": "https://www.accuweather.com/", + "method": "Browser-visible page asset inventory from the rendered AccuWeather homepage", + "assets": { + "static/fonts/Solis-Regular.woff2": "https://www.accuweather.com/fonts/Solis-Regular.woff2", + "static/images/weather-01.svg": "https://www.accuweather.com/images/weathericons/v2a/01.svg", + "static/images/weather-02.svg": "https://www.accuweather.com/images/weathericons/v2a/02.svg", + "static/images/weather-03.svg": "https://www.accuweather.com/images/weathericons/v2a/03.svg", + "static/images/weather-06.svg": "https://www.accuweather.com/images/weathericons/v2a/06.svg", + "static/images/weather-07.svg": "https://www.accuweather.com/images/weathericons/v2a/07.svg", + "static/images/weather-12.svg": "https://www.accuweather.com/images/weathericons/v2a/12.svg", + "static/images/icon-gps.svg": "https://www.awxcdn.com/adc-assets/images/icons/icon-gps.svg" + } +} diff --git a/sites/accuweather/requirements.txt b/sites/accuweather/requirements.txt new file mode 100644 index 000000000..e3e9a71d9 --- /dev/null +++ b/sites/accuweather/requirements.txt @@ -0,0 +1 @@ +Flask diff --git a/sites/accuweather/static/css/site.css b/sites/accuweather/static/css/site.css new file mode 100644 index 000000000..be5d4a8f7 --- /dev/null +++ b/sites/accuweather/static/css/site.css @@ -0,0 +1,7 @@ +@font-face{font-family:Solis;src:url('../fonts/Solis-Regular.woff2')}*{box-sizing:border-box}html,body{margin:0;background:#f1f1f1;color:#292929;font-family:Solis,Arial,sans-serif}a{color:inherit;text-decoration:none}button,.button{border:0;border-radius:2px;background:#f05514;color:#fff;padding:12px 22px;font:inherit;cursor:pointer}input{font:inherit}.corp{height:34px;background:#1d1d1d;color:#bbb;display:flex;justify-content:flex-end;gap:28px;padding:8px calc((100% - 1180px)/2);font-size:12px;white-space:nowrap}.breaking{background:#303030;color:#fff;text-align:center;padding:8px;font-size:13px}header{height:66px;background:#222;color:#fff;display:flex;align-items:center;gap:24px;padding:0 max(24px,calc((100% - 1180px)/2));position:relative;z-index:2}.logo{font-size:28px;white-space:nowrap}.logo b{color:#f25b24}.menu{font-size:22px}.search{display:flex;max-width:470px;flex:1;margin:auto}.search input{min-width:0;flex:1;padding:12px;border:0}.search button{font-size:24px;padding:4px 15px}.premium{color:#ffb294}.flash{background:#fff4d5;border-left:5px solid #f05514;max-width:920px;margin:18px auto 0;padding:14px}main{min-height:70vh}.hero{height:170px;background:linear-gradient(135deg,#3d6685,#acc7d8);display:grid;place-items:center}.hero form{display:flex;width:min(700px,90%)}.hero input{flex:1;min-width:0;padding:18px;border:0;font-size:17px}.layout{max-width:1180px;margin:24px auto;display:grid;grid-template-columns:minmax(0,2fr) minmax(260px,1fr);gap:22px}.narrow{max-width:920px;margin:32px auto;padding:0 18px}.card{background:white;box-shadow:0 2px 5px #0002;padding:22px}.eyebrow{font-size:13px;letter-spacing:.08em;color:#666}.current{display:grid;grid-template-columns:1fr 100px 100px 1fr;align-items:center;gap:20px;border-top:3px solid #f05514}.current h1{font-size:23px;margin:0}.current strong,.reading strong{font-size:62px;font-weight:400}.weather-icon{width:72px;height:72px;object-fit:contain}.city-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}.city{display:grid;grid-template-columns:1fr 55px 60px;align-items:center;gap:8px}.city .weather-icon{width:50px;height:50px}.city span{grid-column:1/-1;color:#666}.tabs{display:flex;overflow-x:auto;background:#fff;border-bottom:1px solid #ddd;margin:20px 0}.tabs a{padding:15px 18px;white-space:nowrap}.tabs a:hover{border-bottom:3px solid #f05514}.weather-main{display:grid;grid-template-columns:1fr 1fr;gap:18px;border-top:3px solid #f05514}.reading{display:flex;align-items:center;justify-content:flex-end}.weather-main dl{grid-column:1/-1;margin:0}.weather-main dl div{display:flex;justify-content:space-between;border-top:1px solid #ddd;padding:10px}.weather-main dd{margin:0;font-weight:bold}.actions,.account-head{display:flex;align-items:center;gap:15px;margin:18px 0}.account-head h1{margin-right:auto}.results{background:#fff;box-shadow:0 2px 5px #0002}.result{display:flex;align-items:center;gap:15px;padding:17px;border-bottom:1px solid #ddd}.result img{width:26px;height:26px}.result span{display:flex;flex-direction:column;flex:1}.result small{color:#666;margin-top:4px}.result i{font-size:28px}.list{padding:0}.list>div{display:grid;grid-template-columns:90px 70px 100px 1fr 150px;align-items:center;gap:12px;padding:15px 20px;border-bottom:1px solid #ddd}.list .weather-icon{width:52px;height:52px}.radar{height:440px;position:relative;overflow:hidden;background:repeating-linear-gradient(0deg,transparent 0 39px,#ffffff30 40px),repeating-linear-gradient(90deg,transparent 0 39px,#ffffff30 40px),linear-gradient(145deg,#406942,#a5bf83 40%,#496b48);color:#fff}.radar:after{content:"";position:absolute;inset:25px;border:1px solid #ffffff66;border-radius:50%}.radar span{position:absolute;left:47%;top:51%;z-index:3;background:#222c;padding:4px}.storm{position:absolute;border-radius:50%;filter:blur(8px);z-index:2}.storm.one{width:200px;height:100px;background:#51d969;left:13%;top:16%;box-shadow:45px 15px 15px #ffdb3b}.storm.two{width:170px;height:80px;background:#33b759;right:10%;bottom:15%;box-shadow:-30px -10px 20px #ff9d22}.legend{text-align:right;padding:10px}.legend i{display:inline-block;width:18px;height:10px;background:#51d969;margin-left:20px}.legend i+*{}.aq{display:flex;align-items:center;gap:25px;border-left:7px solid #65a747}.aq-number{font-size:55px;color:#4d8d31}.meter{height:14px;background:#ddd}.meter i{display:block;height:100%;background:#65a747}.auth{max-width:480px;margin:45px auto}.auth form{display:flex;flex-direction:column;gap:18px}.auth label{display:flex;flex-direction:column;gap:7px}.auth input:not([type=radio]):not([type=checkbox]){padding:12px;border:1px solid #aaa}.auth fieldset{display:flex;flex-direction:column;gap:15px;border:1px solid #ddd}.empty{text-align:center}.prose{margin-top:40px}footer{margin-top:60px;background:#252525;color:#bbb;padding:35px max(24px,calc((100% - 1180px)/2))}footer div{display:flex;gap:25px;color:#fff}.sr{position:absolute;left:-9999px} +@media(max-width:700px){.corp{display:none}.breaking{font-size:11px}.premium{display:none}header{padding:0 12px;gap:10px}.logo{font-size:20px}.search input{width:80px}.layout{display:block;margin:16px}.layout aside{margin-top:18px}.current{grid-template-columns:1fr 60px}.current strong{font-size:48px}.current>div:last-child{grid-column:1/-1}.city-grid{grid-template-columns:1fr}.tabs{margin-left:-18px;margin-right:-18px}.weather-main{grid-template-columns:1fr}.reading{justify-content:flex-start}.list>div{grid-template-columns:55px 50px 70px 1fr}.list>div span:last-child{grid-column:1/-1}.radar{height:330px}.account-head{flex-wrap:wrap}.account-head h1{width:100%}footer div{flex-wrap:wrap}} +@media(max-width:360px){header{gap:6px}.logo{font-size:18px}.menu{display:none}.search button{padding:4px 9px}.current{padding:15px}.city{grid-template-columns:1fr 45px 45px}.narrow{padding:0 10px}.tabs{margin-left:-10px;margin-right:-10px}.tabs a{padding:13px 12px}.list>div{padding:12px 10px;gap:7px}} + +/* Captured AccuWeather proportions: a compact 960px weather canvas on a pale-blue page. */ +html,body{background:#e7edf5}.corp{height:32px;padding:7px 20px;justify-content:flex-start;gap:12px}.corp span+span{border-left:1px solid #777;padding-left:12px}.breaking{position:absolute;left:40%;right:0;top:0;height:32px;background:#f15a18;text-align:left;padding:8px 20px;z-index:4}header{height:62px;padding:0 max(20px,calc((100% - 960px)/2));gap:17px}.logo{font-size:25px}.search{height:34px;max-width:325px;margin-left:auto;margin-right:0}.search input{padding:8px 12px;background:#fff}.search button{font-size:18px;padding:2px 12px}.premium{background:white;color:#f05514;padding:6px 10px;border-radius:18px;font-size:11px}.hero{height:auto;background:transparent;display:block;max-width:632px;margin:22px auto 12px}.hero form{width:100%;height:42px;box-shadow:0 1px 4px #0002}.hero input{padding:12px 15px;font-size:14px}.hero button{padding:8px 18px}.layout{max-width:960px;margin:20px auto;grid-template-columns:632px 304px;gap:24px}.layout>section>h2{font-size:19px}.layout aside{box-shadow:none}.card{box-shadow:none}.current{grid-template-columns:1fr 70px 78px 1fr;padding:18px;border-top:0}.current strong,.reading strong{font-size:52px}.city-grid{display:block}.city{min-height:56px;grid-template-columns:1fr 45px 55px 110px;border-bottom:1px solid #d5d8dc;padding:8px 15px}.city span{grid-column:auto;text-align:right}.narrow{max-width:960px;margin:28px auto;padding:0}.narrow>h1,.narrow>p{max-width:632px}.tabs{border-bottom:1px solid #bbc0c7;background:transparent;margin:0 0 315px}.tabs a{padding:13px 20px;color:#656b72;font-size:14px}.tabs a:first-child{border-bottom:2px solid #f05514}.weather-main{width:632px;max-width:100%;border-top:0;padding:12px}.weather-main>div:first-child{grid-column:1/-1;border-bottom:1px solid #ddd;display:flex;justify-content:space-between}.weather-main>div:first-child .eyebrow{margin:0}.weather-main .reading{justify-content:flex-start}.weather-main dl{display:grid;grid-template-columns:1fr 1fr;gap:0 18px}.weather-main dl div{padding:11px 0}.actions{max-width:632px}.results{max-width:632px}.result{background:#fff}.list{max-width:632px}.aq,.radar{max-width:632px}.homepage-title{font-size:13px;letter-spacing:.08em;color:#666}.news-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:#ddd}.news-grid article{background:#f5f6f7;padding:15px;min-height:120px}.news-grid h3{font-size:14px;margin:0 0 28px}.news-grid small{color:#777} +@media(max-width:700px){.breaking{position:static;height:auto}.corp{display:none}header{height:54px}.menu{order:4}.logo{font-size:21px}.search{position:absolute;top:58px;left:6px;right:6px;max-width:none;height:36px}.premium{display:none}.hero{margin:48px 6px 12px}.layout{margin:12px 6px;display:block}.layout aside{margin-top:12px}.current{grid-template-columns:minmax(0,1fr) 42px 52px;gap:6px}.current>div:last-child{grid-column:1/-1}.city{grid-template-columns:minmax(0,1fr) 38px 42px 76px;gap:5px;font-size:12px}.narrow{padding:0 8px;margin-top:48px}.tabs{margin:0 -8px 210px}.tabs a{padding:12px 14px}.weather-main{width:100%}.weather-main dl{grid-template-columns:1fr}.news-grid{grid-template-columns:repeat(2,1fr)}} diff --git a/sites/accuweather/static/fonts/Solis-Regular.woff2 b/sites/accuweather/static/fonts/Solis-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0133d024767552589a973779fe194a4a145997f8 GIT binary patch literal 22832 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

Saved Locations

{% for l in saved %}{{l.city}}, {{l.region}}{{l.condition}}{{display_temp(l.temp)}}°{% else %}
You have no saved locations.
{% endfor %}
{% endblock %} diff --git a/sites/accuweather/templates/air_quality.html b/sites/accuweather/templates/air_quality.html new file mode 100644 index 000000000..42ce9ac51 --- /dev/null +++ b/sites/accuweather/templates/air_quality.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% from 'macros.html' import tabs %}{% block content %}

{{location.city}} Air Quality

{{tabs(location)}}
{{location.air_quality}}

{{'Good' if location.air_quality<51 else 'Moderate'}}

The air quality is generally acceptable for most individuals.

Current Pollutants

Fine Particulate Matter (PM2.5)

{% endblock %} diff --git a/sites/accuweather/templates/alerts.html b/sites/accuweather/templates/alerts.html new file mode 100644 index 000000000..a00717b8f --- /dev/null +++ b/sites/accuweather/templates/alerts.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% block content %}

{{location.city}} Alerts

Choose the notifications you want to receive.

{% endblock %} diff --git a/sites/accuweather/templates/base.html b/sites/accuweather/templates/base.html new file mode 100644 index 000000000..da4bfb01f --- /dev/null +++ b/sites/accuweather/templates/base.html @@ -0,0 +1,5 @@ +{% block title %}AccuWeather{% endblock %} +
AccuWeather for BusinessAccuWeather APIsPodcast
Breaking Weather: Track the latest tropical developments
+
Premium+{% if current_user %}{{current_user.name}}{% else %}Sign in{% endif %}
+{% with msgs=get_flashed_messages() %}{% if msgs %}
{{msgs[-1]}}
{% endif %}{% endwith %}
{% block content %}{% endblock %}
+
AccuWeatherAboutPrivacy

© 2026 AccuWeather, Inc. All Rights Reserved.

diff --git a/sites/accuweather/templates/daily.html b/sites/accuweather/templates/daily.html new file mode 100644 index 000000000..488ac56b1 --- /dev/null +++ b/sites/accuweather/templates/daily.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block content %}

{{location.city}} Daily Weather

{{tabs(location)}}
{% for d in location.forecasts|sort(attribute='day_index') %}
{{d.label}}{{icon(d.icon,d.condition)}}{{display_temp(d.high)}}° / {{display_temp(d.low)}}°{{d.condition}}{{d.precip}}% precipitation
{% endfor %}
{% endblock %} diff --git a/sites/accuweather/templates/hourly.html b/sites/accuweather/templates/hourly.html new file mode 100644 index 000000000..b9ef92451 --- /dev/null +++ b/sites/accuweather/templates/hourly.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block content %}

{{location.city}} Hourly Weather

{{tabs(location)}}
{% for h in location.hourly|sort(attribute='hour_index') %}
{{h.label}}{{icon(h.icon,h.condition)}}{{display_temp(h.temp)}}°{{h.condition}}{{h.precip}}% precipitation
{% endfor %}
{% endblock %} diff --git a/sites/accuweather/templates/index.html b/sites/accuweather/templates/index.html new file mode 100644 index 000000000..dfb1fb18f --- /dev/null +++ b/sites/accuweather/templates/index.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% from 'macros.html' import icon %}{% block content %}

UNITED STATES WEATHER

{{location.city}}, {{location.region}}

September 9, 2026 · 12:24 PM

{{icon(location.icon,location.condition)}}{{display_temp(location.temp)}}°
{{location.condition}}
RealFeel® {{display_temp(location.realfeel)}}°

United States Weather Conditions

Weather News

Watching the latest weather patterns across the country

2 hours ago

Prepare for dramatic temperature changes this week

4 hours ago

Severe weather tracker and forecast updates

6 hours ago

What to know before your weekend plans

8 hours ago
{% endblock %} diff --git a/sites/accuweather/templates/login.html b/sites/accuweather/templates/login.html new file mode 100644 index 000000000..d1c3540f1 --- /dev/null +++ b/sites/accuweather/templates/login.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% block content %}

Sign in

New here? Create account

{% endblock %} diff --git a/sites/accuweather/templates/macros.html b/sites/accuweather/templates/macros.html new file mode 100644 index 000000000..6b1c9171f --- /dev/null +++ b/sites/accuweather/templates/macros.html @@ -0,0 +1,2 @@ +{% macro icon(code,alt) %}{{alt}}{% endmacro %} +{% macro tabs(loc) %}{% endmacro %} diff --git a/sites/accuweather/templates/radar.html b/sites/accuweather/templates/radar.html new file mode 100644 index 000000000..2bcc9ba18 --- /dev/null +++ b/sites/accuweather/templates/radar.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% from 'macros.html' import tabs %}{% block content %}

{{location.city}} Weather Radar

{{tabs(location)}}
Light rain Heavy rain
{% endblock %} diff --git a/sites/accuweather/templates/register.html b/sites/accuweather/templates/register.html new file mode 100644 index 000000000..0413e4b25 --- /dev/null +++ b/sites/accuweather/templates/register.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% block content %}

Create account

{% endblock %} diff --git a/sites/accuweather/templates/search.html b/sites/accuweather/templates/search.html new file mode 100644 index 000000000..604bd457a --- /dev/null +++ b/sites/accuweather/templates/search.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% block title %}Search | AccuWeather{% endblock %}{% block content %}

Search results for “{{query}}”

{% if locations %}{% else %}

No locations found

Try a city, region, country, or postal code.

{% endif %}
{% endblock %} diff --git a/sites/accuweather/templates/settings.html b/sites/accuweather/templates/settings.html new file mode 100644 index 000000000..c938a82b6 --- /dev/null +++ b/sites/accuweather/templates/settings.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% block content %}

Settings

Temperature unit
{% endblock %} diff --git a/sites/accuweather/templates/static_page.html b/sites/accuweather/templates/static_page.html new file mode 100644 index 000000000..74e084035 --- /dev/null +++ b/sites/accuweather/templates/static_page.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% block content %}

{{title}}

{{copy}}

Weather with you wherever you go

Accurate, local information helps people plan their day with confidence.

{% endblock %} diff --git a/sites/accuweather/templates/weather.html b/sites/accuweather/templates/weather.html new file mode 100644 index 000000000..d15de9ee5 --- /dev/null +++ b/sites/accuweather/templates/weather.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block title %}{{location.city}} Weather | AccuWeather{% endblock %}{% block content %}

{{location.city}}, {{location.region}}

{{location.country}} Weather

{{tabs(location)}}

CURRENT WEATHER

12:24 PM
{{icon(location.icon,location.condition)}}{{display_temp(location.temp)}}°

{{location.condition}}

RealFeel® {{display_temp(location.realfeel)}}°

Wind
{{location.wind}} mph
Humidity
{{location.humidity}}%
Visibility
{{location.visibility}} mi
Pressure
{{'%.2f'|format(location.pressure)}} in
Manage alerts
{% endblock %} diff --git a/websyn_start.sh b/websyn_start.sh index 9539b4f99..40f24edc7 100644 --- a/websyn_start.sh +++ b/websyn_start.sh @@ -5,7 +5,7 @@ set -e SITES=(allrecipes amazon apple arxiv bbc_news booking github google_flights google_map google_search huggingface wolfram_alpha - cambridge_dictionary coursera espn merriam_webster ikea phys_org target ted osu rotten_tomatoes compass walmart_careers) + cambridge_dictionary coursera espn merriam_webster ikea phys_org target ted osu rotten_tomatoes compass walmart_careers accuweather) BASE_PORT=40000 PID_DIR=/tmp/websyn_pids mkdir -p "$PID_DIR" From 5bb9889d62609f73074a81dfda449913dee7ce0a Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Fri, 11 Sep 2026 15:01:36 -0700 Subject: [PATCH 2/9] chore(accuweather): docs/tests sweep for site 25 and asset markers - README / AGENTS / CONTRIBUTING / CLAUDE / agent_demo README: 25 sites, ports 40000-40024 (the PR bumped Dockerfile and the registries but not the shared docs). - walmart_careers and rotten_tomatoes registry tests: accept the 25-site registry; the shared-docs port-range test now derives the range from control_server.SITES (same approach as PR #95) instead of a hardcoded count. - sites/accuweather: add .build-generated-seed and .requires-images so fetch_assets.sh / check_assets.sh treat the site like rotten_tomatoes and walmart_careers (the Dockerfile regenerates instance_seed at build time; weather icons come from the HF bundle). - sites/accuweather/requirements.txt: pin Flask-SQLAlchemy / SQLAlchemy / Werkzeug / Jinja2 that app.py imports (the image already installs them). Port 40024 is provisional: PR #95 (webmd_doctor) claims the same slot. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- AGENTS.md | 12 ++++++------ CLAUDE.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 8 ++++---- agent_demo/README.md | 2 +- sites/accuweather/.build-generated-seed | 1 + sites/accuweather/.requires-images | 1 + sites/accuweather/requirements.txt | 6 +++++- .../tests/test_environment_quality.py | 4 ++-- .../walmart_careers/tests/test_integration.py | 18 ++++++++++++------ 10 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 sites/accuweather/.build-generated-seed create mode 100644 sites/accuweather/.requires-images diff --git a/AGENTS.md b/AGENTS.md index 22696c277..3c84c3b29 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ A coding agent (Claude Code, Cursor, Aider, Codex, ...) is reading this. Read on ## What it is -23 Flask mirror websites (Amazon, GitHub, BBC News, ...) packaged into one Docker image, plus a control plane on `:8101` for resetting per-site state. Used as a deterministic offline environment for web-agent benchmarks. ~3 GB image. +25 Flask mirror websites (Amazon, GitHub, BBC News, ...) packaged into one Docker image, plus a control plane on `:8101` for resetting per-site state. Used as a deterministic offline environment for web-agent benchmarks. ~3 GB image. Two repos: - **code** (this one) — Flask apps, control plane, scripts. @@ -48,17 +48,17 @@ Inside the image, sites live at `/opt/WebSyn//`. The path predates the ren # fresh clone ./scripts/fetch_assets.sh # pulls assets from HF ./scripts/build.sh # docker build -t webharbor:dev . -docker run -d -p 8101:8101 -p 40000-40023:40000-40023 webharbor:dev +docker run -d -p 8101:8101 -p 40000-40024:40000-40024 webharbor:dev ``` Or use the published image directly: ```bash -docker run -d -p 8101:8101 -p 40000-40023:40000-40023 \ +docker run -d -p 8101:8101 -p 40000-40024:40000-40024 \ battalion7244/webharbor:latest ``` -Sites are on `40000`-`40023` in the order declared by `SITES=( ... )` in `websyn_start.sh`. Control plane: +Sites are on `40000`-`40024` in the order declared by `SITES=( ... )` in `websyn_start.sh`. Control plane: | Method | Path | Purpose | |--------|---------------------|-------------------------------------------| @@ -136,13 +136,13 @@ python3 -m py_compile sites//app.py # 3. run on alt ports (don't collide with anything you already have running) docker run -d --rm --name wh-test \ - -p 8201:8101 -p 41000-41023:40000-40023 webharbor:dev + -p 8201:8101 -p 41000-41024:40000-40024 webharbor:dev # 4. control plane healthy, all sites alive curl -s http://localhost:8201/health | python3 -m json.tool | head # 5. every site renders 200 -for p in $(seq 41000 41023); do +for p in $(seq 41000 41024); do curl -so /dev/null -w "$p:%{http_code}\n" http://localhost:$p/ done diff --git a/CLAUDE.md b/CLAUDE.md index 12e3cb0fb..ad0e2fd08 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,4 +16,4 @@ The full agent guide is loaded above via `@AGENTS.md`. The notes below apply onl ## Existing containers -If a container is already running on `:8101` / `:40000-40023`, treat it as the user's working environment — don't `docker stop` or `docker rm` it without explicit confirmation. Spin up your test container under a different name on alt ports (`:8201`, `:41000-41023`). +If a container is already running on `:8101` / `:40000-40024`, treat it as the user's working environment — don't `docker stop` or `docker rm` it without explicit confirmation. Spin up your test container under a different name on alt ports (`:8201`, `:41000-41024`). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f36384cc..1029fa9bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ git clone https://github.com//webharbor && cd webharbor ./scripts/fetch_assets.sh # pull current assets ./scripts/new_site.py mywebsite # OR edit an existing site ./scripts/build.sh && docker run -d --rm \ - -p 8101:8101 -p 40000-40023:40000-40023 webharbor:dev + -p 8101:8101 -p 40000-40024:40000-40024 webharbor:dev # iterate locally... ./scripts/extract_assets.sh ../webharbor-static-pr/ # split assets out diff --git a/README.md b/README.md index a05d266a0..27bac51db 100644 --- a/README.md +++ b/README.md @@ -36,17 +36,17 @@ WebHarbor takes a different approach. We leverage coding agent (e.g., Claude Cod - **Deep features unlocked** — carts, checkouts, accounts, all fully testable - **Evolving** — harder tasks drive richer mirrors; the environment grows with agents - **RL-ready** — sub-second database resets between rollouts -- **Community-driven** — 24 sites today, scaling to 100+ together +- **Community-driven** — 25 sites today, scaling to 100+ together ## 🚀 Quickstart One command to run all web environments: ```bash -docker run -p 8101:8101 -p 40000-40023:40000-40023 battalion7244/webharbor:latest +docker run -p 8101:8101 -p 40000-40024:40000-40024 battalion7244/webharbor:latest ``` -Then point your agent at `http://localhost:40000` through `http://localhost:40023` to explore 24 local mirrors of WebVoyager sites: `Allrecipes, Amazon, Apple, ArXiv, BBC News, Booking, GitHub, Google Flights, Google Maps, Google Search, Hugging Face, Wolfram Alpha, Cambridge Dictionary, Coursera, ESPN, Merriam-Webster, IKEA, Phys.org, Target, TED, Ohio State University, Rotten Tomatoes, Compass, and Walmart Careers`. +Then point your agent at `http://localhost:40000` through `http://localhost:40024` to explore 25 local mirrors of WebVoyager sites: `Allrecipes, Amazon, Apple, ArXiv, BBC News, Booking, GitHub, Google Flights, Google Maps, Google Search, Hugging Face, Wolfram Alpha, Cambridge Dictionary, Coursera, ESPN, Merriam-Webster, IKEA, Phys.org, Target, TED, Ohio State University, Rotten Tomatoes, Compass, Walmart Careers, and AccuWeather`. For sub-second reset between rollouts, expose the control plane and call `/reset/`: @@ -65,7 +65,7 @@ git clone https://github.com/aiming-lab/WebHarbor && cd WebHarbor ## 🤝 Contribute -We have built 23 high-quality mirrors covering the [WebVoyager](https://github.com/MinorJerry/WebVoyager) benchmark. The next goal is **100+ sites**, covering everything in [Online-Mind2Web](https://huggingface.co/datasets/osunlp/Online-Mind2Web). We are inviting the community to build this together. +We have built 25 high-quality mirrors covering the [WebVoyager](https://github.com/MinorJerry/WebVoyager) benchmark. The next goal is **100+ sites**, covering everything in [Online-Mind2Web](https://huggingface.co/datasets/osunlp/Online-Mind2Web). We are inviting the community to build this together. There are two ways to join the author list: diff --git a/agent_demo/README.md b/agent_demo/README.md index 44c25e604..c3d75c28f 100644 --- a/agent_demo/README.md +++ b/agent_demo/README.md @@ -19,7 +19,7 @@ export OPENAI_BASE_URL=https://api.openai.com/v1 # or your Azure / vLLM endpoi ## Run a task -WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40023:40000-40023 battalion7244/webharbor:latest`). +WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40024:40000-40024 battalion7244/webharbor:latest`). Run a single task from a site's `tasks.jsonl`: diff --git a/sites/accuweather/.build-generated-seed b/sites/accuweather/.build-generated-seed new file mode 100644 index 000000000..3ed94ddc9 --- /dev/null +++ b/sites/accuweather/.build-generated-seed @@ -0,0 +1 @@ +This site rebuilds instance_seed/accuweather.db deterministically at image build time (see Dockerfile); the HF bundle carries no seed DB. diff --git a/sites/accuweather/.requires-images b/sites/accuweather/.requires-images new file mode 100644 index 000000000..1d3d5d593 --- /dev/null +++ b/sites/accuweather/.requires-images @@ -0,0 +1 @@ +This site requires static/images (captured AccuWeather weather icons + GPS icon) from the pinned Hugging Face asset bundle. diff --git a/sites/accuweather/requirements.txt b/sites/accuweather/requirements.txt index e3e9a71d9..0e8f2ee89 100644 --- a/sites/accuweather/requirements.txt +++ b/sites/accuweather/requirements.txt @@ -1 +1,5 @@ -Flask +Flask==3.1.0 +Flask-SQLAlchemy==3.1.1 +Werkzeug==3.1.3 +SQLAlchemy==2.0.36 +Jinja2==3.1.4 diff --git a/sites/rotten_tomatoes/tests/test_environment_quality.py b/sites/rotten_tomatoes/tests/test_environment_quality.py index 806af7087..8992a9393 100644 --- a/sites/rotten_tomatoes/tests/test_environment_quality.py +++ b/sites/rotten_tomatoes/tests/test_environment_quality.py @@ -88,9 +88,9 @@ def test_task_manifest_and_registry(self): self.assertEqual(row['web'], 'http://localhost:40021/') self.assertTrue((ROOT / row['verifier_path']).is_file()) self.assertNotIn('answer', row) - self.assertIn('ted osu rotten_tomatoes compass walmart_careers)', (ROOT / 'websyn_start.sh').read_text()) + self.assertIn('ted osu rotten_tomatoes compass walmart_careers', (ROOT / 'websyn_start.sh').read_text()) self.assertIn("'ted', 'osu', 'rotten_tomatoes'", (ROOT / 'control_server.py').read_text()) - self.assertIn('40000-40023', (ROOT / 'Dockerfile').read_text()) + self.assertIn('40000-40024', (ROOT / 'Dockerfile').read_text()) self.assertTrue((SITE / '.build-generated-seed').is_file()) self.assertTrue((SITE / '.requires-images').is_file()) self.assertTrue((SITE / '.requires-external-cache').is_file()) diff --git a/sites/walmart_careers/tests/test_integration.py b/sites/walmart_careers/tests/test_integration.py index 1a6d9f44e..bdca7f05e 100644 --- a/sites/walmart_careers/tests/test_integration.py +++ b/sites/walmart_careers/tests/test_integration.py @@ -11,7 +11,7 @@ "allrecipes", "amazon", "apple", "arxiv", "bbc_news", "booking", "github", "google_flights", "google_map", "google_search", "huggingface", "wolfram_alpha", "cambridge_dictionary", "coursera", "espn", "merriam_webster", "ikea", "phys_org", - "target", "ted", "osu", "rotten_tomatoes", "compass", "walmart_careers", + "target", "ted", "osu", "rotten_tomatoes", "compass", "walmart_careers", "accuweather", ] @@ -33,12 +33,13 @@ def test_exact_24_site_registry_and_port(): assert EXPECTED.index("rotten_tomatoes") + 40000 == 40021 assert EXPECTED.index("compass") + 40000 == 40022 assert EXPECTED.index("walmart_careers") + 40000 == 40023 + assert EXPECTED.index("accuweather") + 40000 == 40024 def test_docker_preserves_current_main_build_gates_and_adds_walmart(): text = (ROOT / "Dockerfile").read_text() - assert "24 Flask mirror sites" in text - assert "EXPOSE 8101 40000-40023" in text + assert "25 Flask mirror sites" in text + assert "EXPOSE 8101 40000-40024" in text assert "check_asset_inventory.py /opt/WebSyn/compass" in text assert "check_asset_inventory.py /opt/WebSyn/walmart_careers" in text assert "walmart_careers/check_tracked_assets.py" in text @@ -65,11 +66,16 @@ def test_assets_pin_is_immutable_merged_revision(): assert (SITE / "tracked_asset_inventory.json").is_file() -def test_shared_documentation_uses_24_site_range(): +def test_shared_documentation_uses_registry_site_range(): + # Derive the expected port range from the control_server SITES registry so + # this test tracks future site additions instead of hardcoding a count. + sites = control_sites() + current = f"40000-{40000 + len(sites) - 1}" + stale = f"40000-{40000 + len(sites) - 2}" for relative in ["README.md", "AGENTS.md", "CONTRIBUTING.md", "CLAUDE.md", "agent_demo/README.md"]: text = (ROOT / relative).read_text() - assert "40000-40022" not in text, relative - assert "40000-40023" in text, relative + assert stale not in text, relative + assert current in text, relative def test_no_merge_conflict_markers_in_release_files(): From 6e2e24b5d6e04ae956b0bfe8e824cfcce4b087ea Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Fri, 11 Sep 2026 15:03:23 -0700 Subject: [PATCH 3/9] feat(accuweather): deterministic verifiers, judge rubrics and task re-anchor Reviewer grading contract for the 20 AccuWeather tasks (CONTRIBUTING "Reviewer role" B/C): - sites/accuweather/verify/verify_lib.py: merriam_webster-style API with walmart_careers hardening: package identity (task id, agent_done, same loopback host+port for every recorded URL, decodable PNGs), navigation gates incl. search-surfaces-target, label-aware numeric matchers that catch value/label swaps, negation-aware phrase matchers, comparison-winner attribution, SQLite snapshot contract (table set, columns, row counts, pinned catalog fingerprint, immutable catalog tables, read-only tables, exact stateful row deltas), werkzeug hash verification with hashlib only. LLM helpers kept for parity; no verdict depends on them. - verify_0.py .. verify_19.py: one deterministic verifier per task, ground truth hardcoded (never in tasks.jsonl). - tasks.jsonl: verifier_path + judge_rubric on every row; task 5 re-anchored from "UV index" (not rendered on any page) to the pressure reading + air-quality value, keeping the 3-page workflow. - verify/tests: stdlib-only harness (fixture DB reproduces the frozen seed fingerprint, agent.py-format trajectories) covering genuine PASS, no-op, wrong task id, shortcut, wrong answers, alternative phrasings, unterminated run, mixed origin, corrupt PNG, schema/catalog/seed drift, collateral writes and stateful mismatch (293 tests), plus run_matrix.py which drives every task through Chromium and grades pass / noop / shortcut / wrong / mismatch run dirs with live DB snapshots (20/20 tasks OK on carcinoma). - verify/README.md: the contract per task. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- sites/accuweather/tasks.jsonl | 40 +- sites/accuweather/verify/README.md | 98 ++ sites/accuweather/verify/tests/_cases.py | 115 +++ sites/accuweather/verify/tests/_support.py | 263 +++++ sites/accuweather/verify/tests/run_matrix.py | 208 ++++ .../accuweather/verify/tests/test_verify_0.py | 81 ++ .../accuweather/verify/tests/test_verify_1.py | 81 ++ .../verify/tests/test_verify_10.py | 81 ++ .../verify/tests/test_verify_11.py | 81 ++ .../verify/tests/test_verify_12.py | 81 ++ .../verify/tests/test_verify_13.py | 81 ++ .../verify/tests/test_verify_14.py | 81 ++ .../verify/tests/test_verify_15.py | 86 ++ .../verify/tests/test_verify_16.py | 81 ++ .../verify/tests/test_verify_17.py | 81 ++ .../verify/tests/test_verify_18.py | 86 ++ .../verify/tests/test_verify_19.py | 81 ++ .../accuweather/verify/tests/test_verify_2.py | 81 ++ .../accuweather/verify/tests/test_verify_3.py | 81 ++ .../accuweather/verify/tests/test_verify_4.py | 81 ++ .../accuweather/verify/tests/test_verify_5.py | 81 ++ .../accuweather/verify/tests/test_verify_6.py | 86 ++ .../accuweather/verify/tests/test_verify_7.py | 86 ++ .../accuweather/verify/tests/test_verify_8.py | 86 ++ .../accuweather/verify/tests/test_verify_9.py | 86 ++ .../verify/tests/test_verify_lib.py | 82 ++ sites/accuweather/verify/verify_0.py | 54 ++ sites/accuweather/verify/verify_1.py | 55 ++ sites/accuweather/verify/verify_10.py | 55 ++ sites/accuweather/verify/verify_11.py | 54 ++ sites/accuweather/verify/verify_12.py | 57 ++ sites/accuweather/verify/verify_13.py | 55 ++ sites/accuweather/verify/verify_14.py | 57 ++ sites/accuweather/verify/verify_15.py | 54 ++ sites/accuweather/verify/verify_16.py | 56 ++ sites/accuweather/verify/verify_17.py | 55 ++ sites/accuweather/verify/verify_18.py | 67 ++ sites/accuweather/verify/verify_19.py | 58 ++ sites/accuweather/verify/verify_2.py | 54 ++ sites/accuweather/verify/verify_3.py | 55 ++ sites/accuweather/verify/verify_4.py | 55 ++ sites/accuweather/verify/verify_5.py | 56 ++ sites/accuweather/verify/verify_6.py | 53 + sites/accuweather/verify/verify_7.py | 53 + sites/accuweather/verify/verify_8.py | 57 ++ sites/accuweather/verify/verify_9.py | 58 ++ sites/accuweather/verify/verify_lib.py | 918 ++++++++++++++++++ 47 files changed, 4472 insertions(+), 20 deletions(-) create mode 100644 sites/accuweather/verify/README.md create mode 100644 sites/accuweather/verify/tests/_cases.py create mode 100644 sites/accuweather/verify/tests/_support.py create mode 100644 sites/accuweather/verify/tests/run_matrix.py create mode 100644 sites/accuweather/verify/tests/test_verify_0.py create mode 100644 sites/accuweather/verify/tests/test_verify_1.py create mode 100644 sites/accuweather/verify/tests/test_verify_10.py create mode 100644 sites/accuweather/verify/tests/test_verify_11.py create mode 100644 sites/accuweather/verify/tests/test_verify_12.py create mode 100644 sites/accuweather/verify/tests/test_verify_13.py create mode 100644 sites/accuweather/verify/tests/test_verify_14.py create mode 100644 sites/accuweather/verify/tests/test_verify_15.py create mode 100644 sites/accuweather/verify/tests/test_verify_16.py create mode 100644 sites/accuweather/verify/tests/test_verify_17.py create mode 100644 sites/accuweather/verify/tests/test_verify_18.py create mode 100644 sites/accuweather/verify/tests/test_verify_19.py create mode 100644 sites/accuweather/verify/tests/test_verify_2.py create mode 100644 sites/accuweather/verify/tests/test_verify_3.py create mode 100644 sites/accuweather/verify/tests/test_verify_4.py create mode 100644 sites/accuweather/verify/tests/test_verify_5.py create mode 100644 sites/accuweather/verify/tests/test_verify_6.py create mode 100644 sites/accuweather/verify/tests/test_verify_7.py create mode 100644 sites/accuweather/verify/tests/test_verify_8.py create mode 100644 sites/accuweather/verify/tests/test_verify_9.py create mode 100644 sites/accuweather/verify/tests/test_verify_lib.py create mode 100644 sites/accuweather/verify/verify_0.py create mode 100644 sites/accuweather/verify/verify_1.py create mode 100644 sites/accuweather/verify/verify_10.py create mode 100644 sites/accuweather/verify/verify_11.py create mode 100644 sites/accuweather/verify/verify_12.py create mode 100644 sites/accuweather/verify/verify_13.py create mode 100644 sites/accuweather/verify/verify_14.py create mode 100644 sites/accuweather/verify/verify_15.py create mode 100644 sites/accuweather/verify/verify_16.py create mode 100644 sites/accuweather/verify/verify_17.py create mode 100644 sites/accuweather/verify/verify_18.py create mode 100644 sites/accuweather/verify/verify_19.py create mode 100644 sites/accuweather/verify/verify_2.py create mode 100644 sites/accuweather/verify/verify_3.py create mode 100644 sites/accuweather/verify/verify_4.py create mode 100644 sites/accuweather/verify/verify_5.py create mode 100644 sites/accuweather/verify/verify_6.py create mode 100644 sites/accuweather/verify/verify_7.py create mode 100644 sites/accuweather/verify/verify_8.py create mode 100644 sites/accuweather/verify/verify_9.py create mode 100644 sites/accuweather/verify/verify_lib.py diff --git a/sites/accuweather/tasks.jsonl b/sites/accuweather/tasks.jsonl index 74f7569cb..d88e91a7e 100644 --- a/sites/accuweather/tasks.jsonl +++ b/sites/accuweather/tasks.jsonl @@ -1,20 +1,20 @@ -{"web_name":"AccuWeather","id":"AccuWeather--0","ques":"Search for Phoenix, Arizona and report the current temperature, RealFeel temperature, and humidity from its current weather page.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--1","ques":"Search for Portland and use the results to open Portland, Maine. Report its current condition, wind speed, and visibility.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--2","ques":"Find Seattle's hourly forecast. Among the displayed hours, identify the first hour with at least a 40% chance of precipitation and report the hour, temperature, and condition.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--3","ques":"Open Miami's daily forecast and identify the displayed day with the greatest chance of precipitation. Report the day, high and low, and condition.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--4","ques":"Compare the current RealFeel temperatures for Austin and Denver. Report both values and which city feels cooler.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--5","ques":"Search for Springfield and open the Missouri result. Report its postal code from the search results, then its UV index and air-quality value from the relevant weather pages.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--6","ques":"Sign in as alice.j@test.com with password TestPass123!, then save Seattle to My AccuWeather. Confirm Seattle appears in Saved Locations.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--7","ques":"Sign in as alice.j@test.com with password TestPass123! and remove Boston from Saved Locations. Confirm Boston is no longer listed there.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--8","ques":"Sign in as bob.smith@test.com with password TestPass123!, open Chicago weather alerts, and enable Severe weather and Rain starting soon only. Save the preferences.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--9","ques":"Sign in as carol.w@test.com with password TestPass123! and change the temperature unit to Celsius. Return to New York current weather and report the displayed temperature and RealFeel in Celsius.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--10","ques":"Use postal-code search for 94102. Open the matching location and report its current condition, humidity, and pressure.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--11","ques":"Find New Orleans and open its radar page. Confirm the radar is for New Orleans, then report the current condition from the Current Weather tab.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--12","ques":"Compare the air-quality values for Los Angeles and San Francisco. Report both values and identify the city with better air quality (the lower value).","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--13","ques":"For Boston's seven displayed daily forecasts, find the day with the lowest overnight low. Report the day, low, high, and condition.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--14","ques":"Search for London, England and report the current temperature, wind, humidity, and air-quality category.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--15","ques":"Sign in as david.b@test.com with password TestPass123!, save Phoenix and Miami, and verify both cities appear in Saved Locations.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--16","ques":"Search for Portland and compare the current temperatures of the Oregon and Maine results. Report both and say which is warmer.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--17","ques":"On Toronto's hourly forecast, find the highest displayed temperature. Report the first hour at that temperature and its precipitation chance.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--18","ques":"Create an account named Jamie Lee using jamie.lee@example.test and password Weather123!, save Atlanta, and confirm it is shown in the new account's Saved Locations.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} -{"web_name":"AccuWeather","id":"AccuWeather--19","ques":"Compare Phoenix and New Orleans using their current weather pages. Report each city's temperature and humidity, then identify which has the higher heat index using RealFeel as the comparison.","web":"http://localhost:40024/","upstream_url":"https://www.accuweather.com/"} +{"web_name": "AccuWeather", "id": "AccuWeather--0", "ques": "Search for Phoenix, Arizona and report the current temperature, RealFeel temperature, and humidity from its current weather page.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_0.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must open the Phoenix, Arizona current-weather page on the mirror (URL path /weather/phoenix-az); answering without that page visit is a FAIL. (2) The final answer must state three values exactly as rendered on that page: the current temperature, the RealFeel temperature and the humidity percentage. (3) A value that differs from the page, or a missing value, is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--1", "ques": "Search for Portland and use the results to open Portland, Maine. Report its current condition, wind speed, and visibility.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_1.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must run a site search whose results include Portland (the search page lists two Portlands) and open the Portland, Maine current-weather page (/weather/portland-me), not the Oregon one. (2) The final answer must give that page's current condition wording, wind speed and visibility exactly as rendered. (3) Values taken from Portland, Oregon are a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--2", "ques": "Find Seattle's hourly forecast. Among the displayed hours, identify the first hour with at least a 40% chance of precipitation and report the hour, temperature, and condition.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_2.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must open the Seattle hourly forecast page (/hourly/seattle-wa). (2) The answer must name the FIRST listed hour whose precipitation chance is at least 40%, plus that row's temperature and condition wording, exactly as rendered. (3) Any other hour, or values from a different row, is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--3", "ques": "Open Miami's daily forecast and identify the displayed day with the greatest chance of precipitation. Report the day, high and low, and condition.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_3.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must open the Miami daily forecast page (/daily/miami-fl). (2) The answer must name the day label with the highest precipitation percentage among the seven rows and give that row's high, low and condition wording exactly as rendered. (3) A different day or mixed-up values is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--4", "ques": "Compare the current RealFeel temperatures for Austin and Denver. Report both values and which city feels cooler.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_4.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must open BOTH current-weather pages, Austin (/weather/austin-tx) and Denver (/weather/denver-co); RealFeel is only shown there. (2) The answer must give each city's RealFeel value as rendered and state which city feels cooler. (3) Swapped values, a missing value, or naming the wrong city as cooler is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--5", "ques": "Search for Springfield and open the Missouri result. Report its postal code from the search results, then its pressure reading from its current weather page and its air-quality value from its Air Quality page.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_5.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must search for Springfield (three same-name results) and open the Missouri result. (2) The answer must give the Missouri result's postal code as shown on the search results page, the pressure reading from /weather/springfield-mo and the air-quality number from /air-quality/springfield-mo; both pages must appear in the trajectory. (3) Values from Springfield, Illinois or Massachusetts are a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--6", "ques": "Sign in as alice.j@test.com with password TestPass123!, then save Seattle to My AccuWeather. Confirm Seattle appears in Saved Locations.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_6.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must sign in through /login as alice.j@test.com. (2) It must open the Seattle current-weather page and use the Save location control, then open My AccuWeather (/account) afterwards. (3) After the run the database must contain exactly one new saved_location row (Alice -> Seattle) and no other saved-location, user or alert change; a self-reported success with an unchanged database is a FAIL. (4) The final answer must confirm Seattle is listed. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--7", "ques": "Sign in as alice.j@test.com with password TestPass123! and remove Boston from Saved Locations. Confirm Boston is no longer listed there.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_7.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must sign in through /login as alice.j@test.com. (2) It must open the Boston current-weather page and use the Remove from saved locations control, then open My AccuWeather (/account) afterwards. (3) After the run Alice's Boston saved_location row must be gone and nothing else may change (her New York row stays; no user or alert change). (4) The final answer must confirm Boston is no longer listed. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--8", "ques": "Sign in as bob.smith@test.com with password TestPass123!, open Chicago weather alerts, and enable Severe weather and Rain starting soon only. Save the preferences.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_8.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must sign in through /login as bob.smith@test.com and open the Chicago alerts page (/alerts/chicago-il). (2) It must submit the alert form with exactly Severe weather and Rain starting soon checked. (3) After the run the alert table must contain exactly those two enabled rows for Bob/Chicago and nothing else; an unchanged table or an extra alert type (e.g. Temperature changes) is a FAIL. (4) No user or saved-location rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--9", "ques": "Sign in as carol.w@test.com with password TestPass123! and change the temperature unit to Celsius. Return to New York current weather and report the displayed temperature and RealFeel in Celsius.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_9.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must sign in through /login as carol.w@test.com, open Settings (/settings) and save Celsius, then open the New York current-weather page (/weather/new-york-ny) AFTER the settings change. (2) Carol's stored unit must be C after the run and no other user, saved-location or alert row may change. (3) The final answer must report the New York temperature and RealFeel as rendered in Celsius; Fahrenheit values are a FAIL. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--10", "ques": "Use postal-code search for 94102. Open the matching location and report its current condition, humidity, and pressure.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_10.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must submit a site search for the postal code 94102 (a /search?q=94102 visit) and open the single matching location's current-weather page. (2) The answer must give that page's current condition wording, humidity percentage and pressure reading exactly as rendered. (3) A qualified condition (e.g. 'Mostly cloudy' instead of the rendered word) or a wrong number is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--11", "ques": "Find New Orleans and open its radar page. Confirm the radar is for New Orleans, then report the current condition from the Current Weather tab.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_11.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must find New Orleans via site search and open its Radar page (/radar/new-orleans-la), whose heading names the city. (2) It must then open the Current Weather tab (/weather/new-orleans-la) and report the current condition wording exactly as rendered. (3) Missing either page visit is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--12", "ques": "Compare the air-quality values for Los Angeles and San Francisco. Report both values and identify the city with better air quality (the lower value).", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_12.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must open BOTH Air Quality pages, Los Angeles (/air-quality/los-angeles-ca) and San Francisco (/air-quality/san-francisco-ca). (2) The answer must give each city's air-quality number as rendered and name the city with the LOWER number as having better air quality. (3) Swapped numbers or the wrong city is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--13", "ques": "For Boston's seven displayed daily forecasts, find the day with the lowest overnight low. Report the day, low, high, and condition.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_13.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must open the Boston daily forecast page (/daily/boston-ma). (2) The answer must name the day label whose low is the smallest among the seven rows and give that row's low, high and condition wording exactly as rendered. (3) A different day or mixed-up values is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--14", "ques": "Search for London, England and report the current temperature, wind, humidity, and air-quality category.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_14.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must find London via site search and open its current-weather page (/weather/london-gb) and its Air Quality page (/air-quality/london-gb). (2) The answer must give the current temperature, wind speed and humidity from the weather page and the air-quality category word from the Air Quality page, exactly as rendered. (3) A missing value or the wrong category word is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--15", "ques": "Sign in as david.b@test.com with password TestPass123!, save Phoenix and Miami, and verify both cities appear in Saved Locations.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_15.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must sign in through /login as david.b@test.com. (2) It must save BOTH Phoenix and Miami from their current-weather pages and open My AccuWeather (/account) afterwards. (3) After the run David must have exactly two new saved_location rows (Phoenix and Miami) and nothing else may change; one missing city or an unchanged database is a FAIL. (4) The final answer must confirm both cities are listed. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--16", "ques": "Search for Portland and compare the current temperatures of the Oregon and Maine results. Report both and say which is warmer.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_16.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must search for Portland and open BOTH results' current-weather pages (/weather/portland-or and /weather/portland-me); temperatures are not shown on the results page. (2) The answer must give each Portland's current temperature as rendered and state which one is warmer. (3) Swapped values or the wrong city is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--17", "ques": "On Toronto's hourly forecast, find the highest displayed temperature. Report the first hour at that temperature and its precipitation chance.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_17.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must find Toronto via site search and open its hourly forecast page (/hourly/toronto-ca). (2) The answer must give the highest temperature listed, the FIRST hour label showing it, and that row's precipitation percentage, exactly as rendered. (3) A different hour or percentage is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--18", "ques": "Create an account named Jamie Lee using jamie.lee@example.test and password Weather123!, save Atlanta, and confirm it is shown in the new account's Saved Locations.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_18.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must create the account through /register with the exact name, email and password given in the task. (2) It must then open the Atlanta current-weather page, use Save location, and open My AccuWeather (/account). (3) After the run the database must contain exactly one new user (that email and name, whose password verifies) and exactly one new saved_location row for that user (Atlanta); seeded users and their saved rows must be untouched. (4) The final answer must confirm Atlanta is listed. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} +{"web_name": "AccuWeather", "id": "AccuWeather--19", "ques": "Compare Phoenix and New Orleans using their current weather pages. Report each city's temperature and humidity, then identify which has the higher heat index using RealFeel as the comparison.", "web": "http://localhost:40024/", "upstream_url": "https://www.accuweather.com/", "verifier_path": "sites/accuweather/verify/verify_19.py", "judge_rubric": "FACT CHECKPOINTS: (1) The agent must open BOTH current-weather pages, Phoenix (/weather/phoenix-az) and New Orleans (/weather/new-orleans-la). (2) The answer must give each city's current temperature and humidity as rendered and name the city with the higher RealFeel value. (3) A missing value or the wrong city is a FAIL. (4) No account, saved-location or alert rows may change. A checkpoint passes only when the required evidence is present in the trajectory, screenshots or database; an empty final answer is a FAIL."} diff --git a/sites/accuweather/verify/README.md b/sites/accuweather/verify/README.md new file mode 100644 index 000000000..430b6d793 --- /dev/null +++ b/sites/accuweather/verify/README.md @@ -0,0 +1,98 @@ +# AccuWeather deterministic grading contract + +Each row in `sites/accuweather/tasks.jsonl` points to `verify_0.py` … `verify_19.py` +(`verifier_path`) and carries an English `judge_rubric` of fact checkpoints. Ground +truth lives ONLY inside the verifiers; `tasks.jsonl` has no `answer` key. No verifier +calls an LLM: `verify_lib.py` keeps `llm_text_match` / `llm_screenshot_shows` for API +parity with `sites/merriam_webster/verify`, but no verdict depends on them and +`--no_llm True` is accepted for CLI parity. + +## Inputs + +```bash +cd agent_demo && uv run python ../sites/accuweather/verify/verify_0.py \ + --run_dir /abs/path/to/run [--initial_db initial.db --after_db after.db] [--no_llm True] +``` + +`agent_demo/eval_judge.py --run_dir --verifier True` invokes the same script. +Snapshots resolve in this order: explicit `--initial_db` / `--after_db`, then +`/initial.db` + `/after.db`, then `docker cp` from +`$WH_CONTAINER` (default `wh-review`) of `/opt/WebSyn/accuweather/{instance_seed,instance}/accuweather.db`. +Missing or invalid snapshots fail closed (`infra_error: true`). Output is JSON +`{task_id, pass, reason, evidence[]}`; exit 0 = PASS, 1 = FAIL. + +## Package validation (every task) + +- exact `task_id`; non-empty `final_answer`; `terminated: true` with + `termination_reason: agent_done`; at least one step; +- every recorded URL (`start_url`, step `url` / `url_before` / `url_after`, `final_url`) + is `http://` on a loopback host with the same host and port as `start_url`; +- both screenshots of every step exist and decode as non-empty PNGs (header + IHDR CRC). + +## Snapshot contract + +Both snapshots must have exactly the six tables `user, location, forecast, hourly, +saved_location, alert` with the expected columns. The initial snapshot must hold +20 locations / 140 forecasts / 240 hourly rows / 4 seeded users / 2 saved rows +(Alice: New York, Boston) / 0 alerts, and its `location + forecast + hourly` rows must +hash to `CATALOG_FINGERPRINT` (sha256 of the build-generated seed; password salts +are excluded so a rebuilt seed still validates). `location`, `forecast` and `hourly` +must be row-identical before and after. Read-only tasks additionally require +`user`, `saved_location` and `alert` to be row-identical. Stateful tasks require the +exact allowed delta and nothing else: + +| task | required navigation (in order where stated) | state delta | +|---|---|---| +| 6 | `/login` → `/weather/seattle-wa` → `/account`, typed `alice.j@test.com` | +1 saved row Alice→Seattle | +| 7 | `/login` → `/weather/boston-ma` → `/account` | −1 saved row Alice→Boston | +| 8 | `/login` → `/alerts/chicago-il`, typed `bob.smith@test.com` | alert rows == {Bob/Chicago/severe, Bob/Chicago/rain}, enabled | +| 9 | `/login` → `/settings` → `/weather/new-york-ny` | Carol `unit` F→C, all other columns/rows identical | +| 15 | `/login` → `/weather/phoenix-az` → `/account` and `/login` → `/weather/miami-fl` → `/account` | +2 saved rows David→Phoenix, David→Miami | +| 18 | `/register` (typed `jamie.lee@example.test`) → `/weather/atlanta-ga` → `/account` | +1 user (email, name "Jamie Lee", password verifies via hashlib scrypt/pbkdf2) and +1 saved row for that user → Atlanta | + +## Navigation gates (anti knowledge-shortcut) + +Every fact page whose values the answer reports must appear in the trajectory +(`/weather/`, `/hourly/`, `/daily/`, `/air-quality/`, +`/radar/`); comparison tasks (4, 12, 16, 19) need both detail pages. When the +target is not linked from the homepage grid (everything except New York, Phoenix, +Seattle, Miami, Chicago, Boston, Austin, Denver) the trajectory must contain a +`/search?q=` visit that would surface the target under the site's own token scoring +(`search_surfaces`); task 10 requires the literal postal-code query `94102`. + +## Answer matchers + +`contains_fact(text, value, unit, label)` accepts a standalone number, a number with +its unit (`104°`, `104 F`, `18%`, `8 mph`, `29.89 in`), or a number bound to its label +(`humidity: 18`); when the answer binds a *different* number to that label +(`temperature 115, RealFeel 104`) the check fails, so value/label swaps are caught +whenever the agent labels its values. Numbers are matched as whole values (`104` +never matches `1040` or `104.5`). Conditions are matched as phrases and single-word +conditions reject qualified variants (`Cloudy` ≠ `Mostly cloudy`). Clock times accept +`4 PM`, `4:00 p.m.`, `16:00`; day labels accept `Sat` / `Saturday`. Comparison tasks use +`names_winner`, which attributes the comparative word (`cooler`, `better`, `warmer`, +`higher`) to the nearest city in the sentence and rejects the inverse attribution. +All matchers are negation-aware (`not 18%` does not count). + +Known limit: when an agent reports two same-unit values with no labels at all +(`103°, 82°`) a swap between them is not detectable; the winner check still has to +name the right city. + +## Tests + +```bash +cd agent_demo && uv run python -m unittest discover -s ../sites/accuweather/verify/tests -p 'test_*.py' +``` + +`tests/_support.py` rebuilds the seed with plain sqlite3 from the same constants and +formulas as `app.py` (`test_verify_lib.py` asserts the fixture reproduces +`CATALOG_FINGERPRINT`) and writes trajectories in the `agent_demo/agent.py` layout. +Every task module covers: genuine PASS, run-dir snapshot discovery, no-op (empty +answer), wrong task id, shortcut (no navigation), wrong answers, alternative +phrasings, unterminated run, mixed origin, corrupt PNG, schema / catalog / seed drift +(fail closed), and read-only collateral writes or stateful mismatch. + +`tests/run_matrix.py` drives every task through a real Chromium against a standalone +copy of the site and grades pass / noop / shortcut / wrong / mismatch run dirs with +live SQLite snapshots (CONTRIBUTING §C). `tests/` is excluded from the Docker image by +`.dockerignore`. diff --git a/sites/accuweather/verify/tests/_cases.py b/sites/accuweather/verify/tests/_cases.py new file mode 100644 index 000000000..6876c9098 --- /dev/null +++ b/sites/accuweather/verify/tests/_cases.py @@ -0,0 +1,115 @@ +"""Per-task genuine trajectories, correct answers, wrong answers and expected gate +names for the accuweather verifier tests and for verify/tests/run_matrix.py.""" +from __future__ import annotations + +from _support import State, login_steps, step + +def _s(*paths): + out = [step("/")] + out += [step(p) for p in paths[:-1]] + out.append(step(paths[-1], "done")) + return out + +CASES = { + 0: dict(steps=_s("/search?q=Phoenix", "/weather/phoenix-az"), + answer="Phoenix: temperature 104°F, RealFeel 115°F, humidity 18%.", + gate="visited_weather_phoenix", + wrong=[("temperature 115°F, RealFeel 104°F, humidity 18%", "answer_temperature"), + ("Phoenix: 104°, RealFeel 115°, humidity 61%", "answer_humidity")], + also_pass=["104° / RealFeel 115° / 18% humidity", "Temp: 104 F; Feels like 115 F; Humidity: 18 percent"]), + 1: dict(steps=_s("/search?q=Portland", "/weather/portland-me"), + answer="Portland, Maine: Mostly cloudy, wind 8 mph, visibility 10 mi.", + gate="searched_for_portland-me", + wrong=[("Portland, Maine: Cloudy, wind 5 mph, visibility 10 mi.", "answer_condition"), + ("Mostly cloudy, wind 10 mph, visibility 8 mi", "answer_wind")], + also_pass=["mostly-cloudy; 8mph winds; 10 miles visibility"]), + 2: dict(steps=_s("/weather/seattle-wa", "/hourly/seattle-wa"), + answer="4 PM — 69°, Showers (47% precipitation).", + gate="visited_hourly_seattle", + wrong=[("3 PM — 68°, Mostly cloudy (36%)", "answer_hour"), ("4 PM, 68°, Showers", "answer_temperature")], + also_pass=["The first hour is 16:00 at 69 degrees with showers"]), + 3: dict(steps=_s("/weather/miami-fl", "/daily/miami-fl"), + answer="Sat: high 86°, low 73°, Mostly cloudy (63% precipitation).", + gate="visited_daily_miami", + wrong=[("Fri: high 88°, low 74°, Partly sunny", "answer_day"), ("Sat: high 73°, low 86°, Mostly cloudy", "answer_high")], + also_pass=["Saturday, 86°/73°, mostly cloudy"]), + 4: dict(steps=_s("/weather/austin-tx", "/", "/weather/denver-co"), + answer="Austin RealFeel 103°, Denver RealFeel 82°; Denver feels cooler.", + gate="visited_weather_austin", + wrong=[("Austin RealFeel 103°, Denver RealFeel 82°; Austin feels cooler.", "answer_names_cooler_city"), + ("Austin 82°, Denver 103°; Denver feels cooler.", "answer_austin_realfeel")], + also_pass=["Austin: 103°F. Denver: 82°F. The cooler city is Denver.", "Denver (82°) feels cooler than Austin (103°)."]), + 5: dict(steps=_s("/search?q=Springfield", "/weather/springfield-mo", "/air-quality/springfield-mo"), + answer="Postal code 65806; pressure 29.89 in; air quality 39.", + gate="searched_for_springfield-mo", + wrong=[("Postal code 62701; pressure 29.91 in; air quality 33.", "answer_postal_code"), + ("65806, pressure 29.98 in, air quality 39", "answer_pressure")], + also_pass=["ZIP 65806 / 29.89 inHg / AQI: 39"]), + 6: dict(steps=login_steps("alice.j@test.com") + [step("/search?q=Seattle"), step("/weather/seattle-wa"), step("/weather/seattle-wa"), step("/account", "done")], + answer="Seattle is now listed in Saved Locations.", gate="visited_login_page", stateful=True, + after=lambda s: s.add_saved("alice.j@test.com", "seattle-wa"), mismatch="saved_rows_added", + wrong=[("Done.", "answer_mentions_seattle")]), + 7: dict(steps=login_steps("alice.j@test.com") + [step("/weather/boston-ma"), step("/weather/boston-ma"), step("/account", "done")], + answer="Boston was removed and is no longer listed.", gate="visited_login_page", stateful=True, + after=lambda s: s.remove_saved("alice.j@test.com", "boston-ma"), mismatch="saved_rows_removed", + wrong=[("Removed.", "answer_mentions_boston")]), + 8: dict(steps=login_steps("bob.smith@test.com") + [step("/weather/chicago-il"), step("/alerts/chicago-il"), step("/alerts/chicago-il", "done")], + answer="Enabled Severe weather and Rain starting soon for Chicago and saved.", gate="visited_login_page", stateful=True, + after=lambda s: s.set_alerts("bob.smith@test.com", "chicago-il", ["severe", "rain"]), mismatch="alerts_exactly_severe_and_rain_for_chicago", + wrong=[]), + 9: dict(steps=login_steps("carol.w@test.com") + [step("/settings"), step("/settings"), step("/weather/new-york-ny", "done")], + answer="New York now shows 26° with RealFeel 28° in Celsius.", gate="visited_login_page", stateful=True, + after=lambda s: s.set_unit("carol.w@test.com", "C"), mismatch="carol_unit_is_celsius", + wrong=[("New York shows 79° with RealFeel 82°.", "answer_temperature_celsius"), ("temperature 28°C, RealFeel 26°C", "answer_temperature_celsius")]), + 10: dict(steps=_s("/search?q=94102", "/weather/san-francisco-ca"), + answer="San Francisco: Cloudy, humidity 75%, pressure 30.05 in.", + gate="searched_postal_code_94102", + wrong=[("San Francisco: Mostly cloudy, humidity 75%, pressure 30.05 in.", "answer_condition"), + ("Cloudy, humidity 57%, pressure 30.05 in", "answer_humidity")], + also_pass=["cloudy / 75 percent humidity / 30.05 inHg"]), + 11: dict(steps=_s("/search?q=New+Orleans", "/weather/new-orleans-la", "/radar/new-orleans-la", "/weather/new-orleans-la"), + answer="Radar page is for New Orleans; current condition: Showers.", + gate="searched_for_new-orleans-la", + wrong=[("Radar confirmed; current condition: Cloudy.", "answer_condition")]), + 12: dict(steps=_s("/search?q=Los+Angeles", "/weather/los-angeles-ca", "/air-quality/los-angeles-ca", "/search?q=San+Francisco", "/weather/san-francisco-ca", "/air-quality/san-francisco-ca"), + answer="Los Angeles 41, San Francisco 18 — San Francisco has better air quality.", + gate="searched_for_los-angeles-ca", + wrong=[("Los Angeles 41, San Francisco 18 — Los Angeles has better air quality.", "answer_names_better_city"), + ("Los Angeles 18, San Francisco 41 — San Francisco has better air quality.", "answer_los_angeles_value")], + also_pass=["LA: 41. SF: 18. The city with better air quality is San Francisco."]), + 13: dict(steps=_s("/weather/boston-ma", "/daily/boston-ma"), + answer="Sat: low 60°, high 73°, Mostly cloudy.", + gate="visited_daily_boston", + wrong=[("Fri: low 61°, high 75°, Partly sunny.", "answer_day"), ("Sat: low 61°, high 73°, Mostly cloudy.", "answer_low")], + also_pass=["Saturday — 73°/60°, mostly cloudy"]), + 14: dict(steps=_s("/search?q=London", "/weather/london-gb", "/air-quality/london-gb"), + answer="London: 63°, wind 10 mph, humidity 72%, air quality category Good.", + gate="searched_for_london-gb", + wrong=[("London: 63°, wind 10 mph, humidity 72%, air quality Moderate.", "answer_air_quality_category"), + ("London: 62°, wind 10 mph, humidity 72%, Good.", "answer_temperature")]), + 15: dict(steps=login_steps("david.b@test.com") + [step("/weather/phoenix-az"), step("/weather/phoenix-az"), step("/search?q=Miami"), step("/weather/miami-fl"), step("/weather/miami-fl"), step("/account", "done")], + answer="Phoenix and Miami both appear in Saved Locations.", gate="visited_login_page", stateful=True, + after=lambda s: (s.add_saved("david.b@test.com", "phoenix-az"), s.add_saved("david.b@test.com", "miami-fl")), mismatch="saved_rows_added", + wrong=[("Phoenix is saved.", "answer_mentions_both_cities")]), + 16: dict(steps=_s("/search?q=Portland", "/weather/portland-or", "/search?q=Portland", "/weather/portland-me"), + answer="Portland, Oregon 69°; Portland, Maine 70°. Maine is warmer.", + gate="searched_for_portland-or", + wrong=[("Portland, Oregon 69°; Portland, Maine 70°. Oregon is warmer.", "answer_names_warmer_city"), + ("Portland, Oregon 70°; Portland, Maine 69°. Maine is warmer.", "answer_oregon_temperature")], + also_pass=["Oregon: 69°F, Maine: 70°F — Portland, Maine is the warmer one."]), + 17: dict(steps=_s("/search?q=Toronto", "/weather/toronto-ca", "/hourly/toronto-ca"), + answer="Highest 73°, first at 4 PM, precipitation 64%.", + gate="searched_for_toronto-ca", + wrong=[("Highest 73°, first at 3 PM, precipitation 53%.", "answer_hour"), ("73° at 4 PM, precipitation 9%", "answer_precipitation")]), + 18: dict(steps=[step("/"), step("/login"), step("/register"), step("/register", "input", "Jamie Lee"), step("/register", "input", "jamie.lee@example.test"), + step("/register", "input", "Weather123!"), step("/register"), step("/account"), step("/search?q=Atlanta"), step("/weather/atlanta-ga"), + step("/weather/atlanta-ga"), step("/account", "done")], + answer="Atlanta is shown in Saved Locations for the new Jamie Lee account.", gate="visited_register", stateful=True, + after=lambda s: (s.add_user("jamie.lee@example.test", "Jamie Lee", "Weather123!"), s.add_saved("jamie.lee@example.test", "atlanta-ga")), mismatch="exactly_one_new_user", + wrong=[("Account created.", "answer_mentions_atlanta")]), + 19: dict(steps=_s("/weather/phoenix-az", "/search?q=New+Orleans", "/weather/new-orleans-la"), + answer="Phoenix: 104°, humidity 18%. New Orleans: 89°, humidity 75%. Phoenix has the higher heat index (RealFeel 115° vs 100°).", + gate="searched_for_new-orleans-la", + wrong=[("Phoenix: 104°, humidity 18%. New Orleans: 89°, humidity 75%. New Orleans has the higher heat index.", "answer_names_higher_heat_index_city"), + ("Phoenix: 104°, humidity 81%. New Orleans: 89°, humidity 75%. Phoenix is higher.", "answer_phoenix_humidity")]), +} diff --git a/sites/accuweather/verify/tests/_support.py b/sites/accuweather/verify/tests/_support.py new file mode 100644 index 000000000..a12935650 --- /dev/null +++ b/sites/accuweather/verify/tests/_support.py @@ -0,0 +1,263 @@ +"""Shared fixtures for the accuweather verifier tests. + +Synthetic SQLite snapshots with the exact schema of the build-generated seed +(``sites/accuweather/app.py`` seeds users / locations / forecasts / hourly rows +from constants; the same formulas are replicated here with plain sqlite3), plus +a hand-written trajectory writer in the agent_demo/agent.py format. No Flask, +no docker, no LLM. ``test_verify_lib.py`` proves the fixture reproduces the +frozen catalog fingerprint pinned in ``verify_lib.CATALOG_FINGERPRINT``. +""" +from __future__ import annotations + +import copy +import hashlib +import json +import shutil +import sqlite3 +import struct +import subprocess +import sys +import tempfile +import unittest +import zlib +from pathlib import Path +from typing import Any + +VERIFY_DIR = Path(__file__).resolve().parents[1] +SITE_DIR = VERIFY_DIR.parent +BASE = "http://localhost:41024" +PASSWORD = "TestPass123!" + +SCHEMA = """ +CREATE TABLE user (id INTEGER NOT NULL, email VARCHAR(120) NOT NULL, name VARCHAR(80) NOT NULL, + password_hash VARCHAR(255) NOT NULL, unit VARCHAR(1) NOT NULL, PRIMARY KEY (id), UNIQUE (email)); +CREATE TABLE location (id INTEGER NOT NULL, slug VARCHAR(100) NOT NULL, city VARCHAR(80) NOT NULL, + region VARCHAR(80) NOT NULL, country VARCHAR(80) NOT NULL, postal VARCHAR(20) NOT NULL, "temp" INTEGER NOT NULL, + realfeel INTEGER NOT NULL, condition VARCHAR(80) NOT NULL, icon VARCHAR(2) NOT NULL, humidity INTEGER NOT NULL, + wind INTEGER NOT NULL, visibility INTEGER NOT NULL, pressure FLOAT NOT NULL, uv INTEGER NOT NULL, + air_quality INTEGER NOT NULL, PRIMARY KEY (id), UNIQUE (slug)); +CREATE TABLE forecast (id INTEGER NOT NULL, location_id INTEGER NOT NULL, day_index INTEGER NOT NULL, + label VARCHAR(30) NOT NULL, high INTEGER NOT NULL, low INTEGER NOT NULL, condition VARCHAR(80) NOT NULL, + icon VARCHAR(2) NOT NULL, precip INTEGER NOT NULL, PRIMARY KEY (id), FOREIGN KEY(location_id) REFERENCES location (id)); +CREATE TABLE hourly (id INTEGER NOT NULL, location_id INTEGER NOT NULL, hour_index INTEGER NOT NULL, + label VARCHAR(20) NOT NULL, "temp" INTEGER NOT NULL, condition VARCHAR(80) NOT NULL, icon VARCHAR(2) NOT NULL, + precip INTEGER NOT NULL, PRIMARY KEY (id), FOREIGN KEY(location_id) REFERENCES location (id)); +CREATE TABLE saved_location (id INTEGER NOT NULL, user_id INTEGER NOT NULL, location_id INTEGER NOT NULL, + PRIMARY KEY (id), UNIQUE (user_id, location_id), FOREIGN KEY(user_id) REFERENCES user (id), + FOREIGN KEY(location_id) REFERENCES location (id)); +CREATE TABLE alert (id INTEGER NOT NULL, user_id INTEGER NOT NULL, location_id INTEGER NOT NULL, + alert_type VARCHAR(30) NOT NULL, enabled BOOLEAN NOT NULL, PRIMARY KEY (id), + UNIQUE (user_id, location_id, alert_type), FOREIGN KEY(user_id) REFERENCES user (id), + FOREIGN KEY(location_id) REFERENCES location (id)); +""" + +USERS = [("alice.j@test.com", "Alice Johnson"), ("bob.smith@test.com", "Bob Smith"), + ("carol.w@test.com", "Carol Williams"), ("david.b@test.com", "David Brown")] +# (slug, city, region, country, postal, temp, realfeel, condition, icon, humidity, wind, visibility, pressure, uv, air_quality) +LOCATIONS = [ + ("new-york-ny", "New York", "New York", "United States", "10007", 79, 82, "Partly sunny", "03", 61, 9, 10, 29.92, 5, 42), + ("phoenix-az", "Phoenix", "Arizona", "United States", "85001", 104, 115, "Sunny", "01", 18, 7, 12, 29.75, 10, 58), + ("seattle-wa", "Seattle", "Washington", "United States", "98101", 66, 65, "Cloudy", "07", 73, 6, 9, 30.08, 3, 24), + ("miami-fl", "Miami", "Florida", "United States", "33101", 88, 99, "Mostly cloudy", "06", 76, 12, 8, 29.88, 7, 36), + ("chicago-il", "Chicago", "Illinois", "United States", "60601", 72, 71, "Showers", "12", 70, 14, 7, 29.86, 2, 31), + ("boston-ma", "Boston", "Massachusetts", "United States", "02108", 75, 76, "Mostly sunny", "02", 55, 11, 10, 30.01, 6, 28), + ("austin-tx", "Austin", "Texas", "United States", "78701", 96, 103, "Sunny", "01", 38, 10, 11, 29.81, 9, 49), + ("denver-co", "Denver", "Colorado", "United States", "80202", 84, 82, "Partly sunny", "03", 27, 13, 15, 30.04, 8, 45), + ("portland-or", "Portland", "Oregon", "United States", "97205", 69, 68, "Cloudy", "07", 67, 5, 10, 30.11, 3, 22), + ("portland-me", "Portland", "Maine", "United States", "04101", 70, 69, "Mostly cloudy", "06", 64, 8, 10, 30.02, 4, 20), + ("springfield-il", "Springfield", "Illinois", "United States", "62701", 76, 77, "Partly sunny", "03", 59, 10, 10, 29.91, 5, 33), + ("springfield-ma", "Springfield", "Massachusetts", "United States", "01103", 73, 73, "Showers", "12", 69, 7, 8, 29.98, 3, 25), + ("springfield-mo", "Springfield", "Missouri", "United States", "65806", 81, 84, "Mostly sunny", "02", 53, 9, 11, 29.89, 6, 39), + ("san-francisco-ca", "San Francisco", "California", "United States", "94102", 65, 64, "Cloudy", "07", 75, 12, 10, 30.05, 3, 18), + ("los-angeles-ca", "Los Angeles", "California", "United States", "90012", 78, 79, "Mostly sunny", "02", 49, 6, 12, 29.96, 7, 41), + ("atlanta-ga", "Atlanta", "Georgia", "United States", "30303", 86, 91, "Partly sunny", "03", 58, 8, 9, 29.94, 6, 46), + ("nashville-tn", "Nashville", "Tennessee", "United States", "37219", 84, 88, "Mostly cloudy", "06", 61, 7, 10, 29.93, 5, 38), + ("new-orleans-la", "New Orleans", "Louisiana", "United States", "70112", 89, 100, "Showers", "12", 75, 9, 7, 29.87, 4, 44), + ("london-gb", "London", "England", "United Kingdom", "SW1A", 63, 62, "Cloudy", "07", 72, 10, 8, 30.10, 2, 21), + ("toronto-ca", "Toronto", "Ontario", "Canada", "M5H", 70, 69, "Partly sunny", "03", 62, 11, 10, 29.99, 4, 29), +] +HOUR_LABELS = ["Now", "1 PM", "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM", "9 PM", "10 PM", "11 PM"] +DAY_LABELS = ["Today", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue"] +SLUG_ID = {row[0]: i for i, row in enumerate(LOCATIONS, 1)} +EMAIL_ID = {email: i for i, (email, _) in enumerate(USERS, 1)} + + +def scrypt_hash(password: str, salt: str = "fixturesaltAAAA") -> str: + digest = hashlib.scrypt(password.encode(), salt=salt.encode(), n=32768, r=8, p=1, + maxmem=132 * 1024 * 1024, dklen=64).hex() + return f"scrypt:32768:8:1${salt}${digest}" + + +_HASH_CACHE: dict[str, str] = {} + + +def cached_hash(password: str) -> str: + if password not in _HASH_CACHE: + _HASH_CACHE[password] = scrypt_hash(password) + return _HASH_CACHE[password] + + +def build_seed(path: Path) -> Path: + """Replicates app.py's seed_benchmark_users / seed_database / seed_preferences.""" + con = sqlite3.connect(path) + con.executescript(SCHEMA) + for i, (email, name) in enumerate(USERS, 1): + con.execute("INSERT INTO user VALUES (?,?,?,?,?)", (i, email, name, cached_hash(PASSWORD), "F")) + fid = hid = 1 + for lid, row in enumerate(LOCATIONS, 1): + con.execute("INSERT INTO location VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (lid, *row)) + temp, condition, icon = row[5], row[7], row[8] + icons = [icon, "02", "03", "06", "12", "07", "01"] + conditions = [condition, "Mostly sunny", "Partly sunny", "Mostly cloudy", "Showers", "Cloudy", "Sunny"] + for i, label in enumerate(DAY_LABELS): + con.execute("INSERT INTO forecast VALUES (?,?,?,?,?,?,?,?,?)", + (fid, lid, i, label, temp + (i % 3) - 2, temp - 12 - (i % 4), conditions[i], icons[i], (i * 17 + lid * 3) % 71)) + fid += 1 + for i in range(12): + con.execute("INSERT INTO hourly VALUES (?,?,?,?,?,?,?,?)", + (hid, lid, i, HOUR_LABELS[i], temp + (3 - abs(i - 4)), conditions[i % 7], icons[i % 7], (i * 11 + lid) % 66)) + hid += 1 + con.execute("INSERT INTO saved_location VALUES (1, 1, ?)", (SLUG_ID["new-york-ny"],)) + con.execute("INSERT INTO saved_location VALUES (2, 1, ?)", (SLUG_ID["boston-ma"],)) + con.commit() + con.close() + return path + + +class State: + """Mutable view of the user / saved_location / alert tables.""" + + def __init__(self) -> None: + self.users = [dict(id=i, email=e, name=n, password=PASSWORD, unit="F") for i, (e, n) in enumerate(USERS, 1)] + self.saved = [(1, 1, SLUG_ID["new-york-ny"]), (2, 1, SLUG_ID["boston-ma"])] + self.alerts: list[tuple[int, int, int, str, int]] = [] + self.extra_sql: list[str] = [] + + def uid(self, email: str) -> int: + for u in self.users: + if u["email"] == email: + return u["id"] + raise KeyError(email) + + def add_user(self, email: str, name: str, password: str) -> int: + new_id = max(u["id"] for u in self.users) + 1 + self.users.append(dict(id=new_id, email=email, name=name, password=password, unit="F")) + return new_id + + def set_unit(self, email: str, unit: str) -> None: + for u in self.users: + if u["email"] == email: + u["unit"] = unit + + def add_saved(self, email: str, slug: str) -> None: + new_id = max((r[0] for r in self.saved), default=0) + 1 + self.saved.append((new_id, self.uid(email), SLUG_ID[slug])) + + def remove_saved(self, email: str, slug: str) -> None: + before = len(self.saved) + self.saved = [r for r in self.saved if not (r[1] == self.uid(email) and r[2] == SLUG_ID[slug])] + assert len(self.saved) == before - 1, f"no saved row {email}/{slug}" + + def set_alerts(self, email: str, slug: str, types: list[str]) -> None: + uid, lid = self.uid(email), SLUG_ID[slug] + self.alerts = [a for a in self.alerts if not (a[1] == uid and a[2] == lid)] + for kind in sorted(types): + self.alerts.append((max((a[0] for a in self.alerts), default=0) + 1, uid, lid, kind, 1)) + + def write(self, path: Path) -> Path: + build_seed(path) + con = sqlite3.connect(path) + con.execute("DELETE FROM alert"); con.execute("DELETE FROM saved_location"); con.execute("DELETE FROM user") + for u in self.users: + con.execute("INSERT INTO user VALUES (?,?,?,?,?)", (u["id"], u["email"], u["name"], cached_hash(u["password"]), u["unit"])) + con.executemany("INSERT INTO saved_location VALUES (?,?,?)", self.saved) + con.executemany("INSERT INTO alert VALUES (?,?,?,?,?)", self.alerts) + for sql in self.extra_sql: + con.execute(sql) + con.commit(); con.close() + return path + + +def make_png(width: int = 2, height: int = 2) -> bytes: + raw = b"".join(b"\x00" + b"\x10\x20\x30" * width for _ in range(height)) + + def chunk(kind: bytes, data: bytes) -> bytes: + return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data) & 0xFFFFFFFF) + return (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)) + + chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b"")) + + +PNG = make_png() + + +def step(path: str, action: str = "click", text: str | None = None) -> dict[str, Any]: + return {"path": path, "action": action, "text": text} + + +def login_steps(email: str, password: str = PASSWORD) -> list[dict[str, Any]]: + return [step("/"), step("/login"), step("/login", "input", email), step("/login", "input", password), + step("/login"), step("/account")] + + +def write_run(run_dir: Path, task_id: str, steps: list[dict[str, Any]], answer: str, *, + base: str = BASE, start_url: str | None = None, updates: dict[str, Any] | None = None, + corrupt_screenshot: bool = False) -> Path: + shots = run_dir / "screenshots" + shots.mkdir(parents=True, exist_ok=True) + logged = [] + for i, s in enumerate(steps): + if s["action"] == "input": + params: dict[str, Any] = {"index": 1, "text": s["text"]} + elif s["action"] == "done": + params = {"text": answer, "success": True} + elif s["action"] == "navigate": + params = {"url": base + s["path"]} + else: + params = {"index": 1} + logged.append({"step": i, "url": base + s["path"], "title": "AccuWeather", "thought": "", "action": s["action"], + "params": params, "screenshot_before": f"step_{i:03d}.png", "screenshot_after": f"step_{i + 1:03d}.png"}) + for i in range(len(steps) + 1): + (shots / f"step_{i:03d}.png").write_bytes(PNG) + if corrupt_screenshot: + (shots / "step_001.png").write_bytes(b"definitely not a png") + traj = {"task_id": task_id, "task": "", "start_url": start_url or (base + "/"), "max_steps": 15, "steps": logged, + "terminated": True, "termination_reason": "agent_done", "final_answer": answer, "success_self_report": True, + "verifier_path": f"sites/accuweather/verify/verify_{task_id.rsplit('--', 1)[1]}.py", "judge_rubric": ""} + traj.update(updates or {}) + (run_dir / "trajectory.json").write_text(json.dumps(traj, indent=2)) + return run_dir + + +class VerifierTestCase(unittest.TestCase): + N = -1 + + def setUp(self) -> None: + self.tmp = Path(tempfile.mkdtemp(prefix=f"aw_verify_{self.N}_")) + self.addCleanup(shutil.rmtree, self.tmp, ignore_errors=True) + + def verdict(self, steps, answer, *, initial: State | None = None, after: State | None = None, + task_id: str | None = None, snapshots_in_run_dir: bool = False, **kw) -> dict[str, Any]: + run_dir = self.tmp / f"run_{len(list(self.tmp.iterdir()))}" + run_dir.mkdir() + write_run(run_dir, task_id or f"AccuWeather--{self.N}", steps, answer, **kw) + initial_db = (initial or State()).write(run_dir / ("initial.db" if snapshots_in_run_dir else "seed_initial.db")) + after_db = (after or initial or State()).write(run_dir / ("after.db" if snapshots_in_run_dir else "seed_after.db")) + cmd = [sys.executable, str(VERIFY_DIR / f"verify_{self.N}.py"), "--run_dir", str(run_dir)] + if not snapshots_in_run_dir: + cmd += ["--initial_db", str(initial_db), "--after_db", str(after_db)] + r = subprocess.run(cmd, capture_output=True, text=True) + try: + out = json.loads(r.stdout) + except json.JSONDecodeError: + self.fail(f"verifier produced no JSON (rc={r.returncode}): {r.stderr[-800:]}") + out["_rc"] = r.returncode + return out + + def assertPasses(self, v: dict[str, Any]) -> None: + self.assertTrue(v["pass"], "\n".join(v["evidence"])) + self.assertEqual(v["_rc"], 0) + + def assertFailsOn(self, v: dict[str, Any], reason: str) -> None: + self.assertFalse(v["pass"], "expected FAIL but passed") + self.assertEqual(v["_rc"], 1) + self.assertEqual(v["reason"], reason, "\n".join(v["evidence"])) diff --git a/sites/accuweather/verify/tests/run_matrix.py b/sites/accuweather/verify/tests/run_matrix.py new file mode 100644 index 000000000..e81742038 --- /dev/null +++ b/sites/accuweather/verify/tests/run_matrix.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +"""CONTRIBUTING §C validation matrix, driven through a real Chromium. + +For every task: boot the mirror from a fresh copy of instance_seed, drive the +genuine workflow with Playwright (real clicks / form submits, screenshots in +the agent_demo/agent.py layout), snapshot the live SQLite DB as after.db, then +grade five run variants with the task's verifier: + + pass genuine trajectory + correct answer + real after-state -> PASS + noop homepage only, empty answer, clean DB -> FAIL + shortcut correct answer, no on-site navigation -> FAIL + wrong genuine trajectory, wrong answer -> FAIL + mismatch (stateful) genuine trajectory + correct answer, seed DB -> FAIL + +Usage (inside the agent_demo uv env, site venv python for Flask): + uv run python run_matrix.py --site_dir --python --port 45002 --out +""" +from __future__ import annotations + +import argparse +import json +import shutil +import subprocess +import sys +import time +import urllib.request +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from _cases import CASES # noqa: E402 + +HERE = Path(__file__).resolve().parent +VERIFY_DIR = HERE.parent +PASSWORD = "TestPass123!" + +# Browser actions per task: (kind, arg[, text]). "goto" navigates, "click"/"check"/"fill" act on the current page. +LOGIN = lambda email: [("goto", "/login"), ("fill", "input[name=email]", email), ("fill", "input[name=password]", PASSWORD), ("click", "button:has-text('Sign in')")] +ACTIONS = { + 0: [("goto", "/"), ("fill", "#site-search", "Phoenix"), ("click", "header .search button"), ("click", ".result")], + 1: [("goto", "/"), ("fill", "#site-search", "Portland"), ("click", "header .search button"), ("click", ".result:has-text('Maine')")], + 2: [("goto", "/"), ("click", "a.city[href='/weather/seattle-wa']"), ("click", ".tabs a:has-text('Hourly')")], + 3: [("goto", "/"), ("click", "a.city[href='/weather/miami-fl']"), ("click", ".tabs a:has-text('Daily')")], + 4: [("goto", "/"), ("click", "a.city[href='/weather/austin-tx']"), ("goto", "/"), ("click", "a.city[href='/weather/denver-co']")], + 5: [("goto", "/"), ("fill", "#site-search", "Springfield"), ("click", "header .search button"), ("click", ".result:has-text('Missouri')"), ("click", ".tabs a:has-text('Air Quality')")], + 6: LOGIN("alice.j@test.com") + [("fill", "#site-search", "Seattle"), ("click", "header .search button"), ("click", ".result"), ("click", ".actions form button"), ("click", "header a[href='/account']")], + 7: LOGIN("alice.j@test.com") + [("click", ".results .result:has-text('Boston')"), ("click", ".actions form button"), ("click", "header a[href='/account']")], + 8: LOGIN("bob.smith@test.com") + [("fill", "#site-search", "Chicago"), ("click", "header .search button"), ("click", ".result"), ("click", ".actions a.button"), ("check", "input[value=severe]"), ("check", "input[value=rain]"), ("click", "button:has-text('Save alerts')")], + 9: LOGIN("carol.w@test.com") + [("click", "a[href='/settings']"), ("check", "input[value=C]"), ("click", "button:has-text('Save settings')"), ("goto", "/"), ("click", "a.city[href='/weather/new-york-ny']")], + 10: [("goto", "/"), ("fill", ".hero input[name=q]", "94102"), ("click", ".hero button"), ("click", ".result")], + 11: [("goto", "/"), ("fill", "#site-search", "New Orleans"), ("click", "header .search button"), ("click", ".result:has-text('Orleans')"), ("click", ".tabs a:has-text('Radar')"), ("click", ".tabs a:has-text('Current Weather')")], + 12: [("goto", "/"), ("fill", "#site-search", "Los Angeles"), ("click", "header .search button"), ("click", ".result:has-text('Los Angeles')"), ("click", ".tabs a:has-text('Air Quality')"), ("fill", "#site-search", "San Francisco"), ("click", "header .search button"), ("click", ".result:has-text('San Francisco')"), ("click", ".tabs a:has-text('Air Quality')")], + 13: [("goto", "/"), ("click", "a.city[href='/weather/boston-ma']"), ("click", ".tabs a:has-text('Daily')")], + 14: [("goto", "/"), ("fill", "#site-search", "London"), ("click", "header .search button"), ("click", ".result"), ("click", ".tabs a:has-text('Air Quality')")], + 15: LOGIN("david.b@test.com") + [("goto", "/"), ("click", "a.city[href='/weather/phoenix-az']"), ("click", ".actions form button"), ("goto", "/"), ("click", "a.city[href='/weather/miami-fl']"), ("click", ".actions form button"), ("click", "header a[href='/account']")], + 16: [("goto", "/"), ("fill", "#site-search", "Portland"), ("click", "header .search button"), ("click", ".result:has-text('Oregon')"), ("fill", "#site-search", "Portland"), ("click", "header .search button"), ("click", ".result:has-text('Maine')")], + 17: [("goto", "/"), ("fill", "#site-search", "Toronto"), ("click", "header .search button"), ("click", ".result"), ("click", ".tabs a:has-text('Hourly')")], + 18: [("goto", "/"), ("click", "header a[href='/login']"), ("click", "a[href='/register']"), ("fill", "input[name=name]", "Jamie Lee"), ("fill", "input[name=email]", "jamie.lee@example.test"), ("fill", "input[name=password]", "Weather123!"), ("click", "button:has-text('Create account')"), ("fill", "#site-search", "Atlanta"), ("click", "header .search button"), ("click", ".result"), ("click", ".actions form button"), ("click", "header a[href='/account']")], + 19: [("goto", "/"), ("click", "a.city[href='/weather/phoenix-az']"), ("fill", "#site-search", "New Orleans"), ("click", "header .search button"), ("click", ".result:has-text('Orleans')")], +} + + +def wait_http(url: str, timeout: float = 15.0) -> None: + deadline = time.time() + timeout + while time.time() < deadline: + try: + urllib.request.urlopen(url, timeout=2) + return + except Exception: + time.sleep(0.3) + raise RuntimeError(f"site did not come up: {url}") + + +class Site: + def __init__(self, site_dir: Path, python: str, port: int): + self.site_dir, self.python, self.port, self.proc = site_dir, python, port, None + + def start(self) -> None: + inst, seed = self.site_dir / "instance", self.site_dir / "instance_seed" + shutil.rmtree(inst, ignore_errors=True) + shutil.copytree(seed, inst) + self.proc = subprocess.Popen([self.python, "-c", f"from app import app; app.run(host='127.0.0.1', port={self.port}, debug=False, use_reloader=False)"], + cwd=self.site_dir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + wait_http(f"http://127.0.0.1:{self.port}/") + + def stop(self) -> None: + if self.proc: + self.proc.kill(); self.proc.wait(); self.proc = None + + @property + def base(self) -> str: + return f"http://127.0.0.1:{self.port}" + + +def drive(page, base: str, actions, shots: Path, answer: str) -> list[dict]: + steps = [] + idx = 0 + page.screenshot(path=str(shots / f"step_{idx:03d}.png")) + for act in actions: + kind, arg = act[0], act[1] + url_before = page.url + if kind == "goto": + logged = {"action": "navigate", "params": {"url": base + arg}} + elif kind == "fill": + logged = {"action": "input", "params": {"index": 1, "text": act[2]}} + else: + logged = {"action": "click", "params": {"index": 1}} + steps.append({"step": idx, "url": url_before, "title": page.title(), "thought": "", **logged, + "screenshot_before": f"step_{idx:03d}.png", "screenshot_after": f"step_{idx + 1:03d}.png"}) + if kind == "goto": + page.goto(base + arg, wait_until="networkidle") + elif kind == "fill": + page.fill(arg, act[2]) + elif kind == "check": + page.check(arg) + else: + page.click(arg); page.wait_for_load_state("networkidle") + idx += 1 + page.screenshot(path=str(shots / f"step_{idx:03d}.png")) + steps.append({"step": idx, "url": page.url, "title": page.title(), "thought": "", "action": "done", + "params": {"text": answer, "success": True}, "screenshot_before": f"step_{idx:03d}.png", "screenshot_after": f"step_{idx + 1:03d}.png"}) + page.screenshot(path=str(shots / f"step_{idx + 1:03d}.png")) + return steps + + +def write_traj(run_dir: Path, task_id: str, base: str, steps: list[dict], answer: str) -> None: + n = int(task_id.rsplit("--", 1)[1]) + traj = {"task_id": task_id, "task": "", "start_url": base + "/", "max_steps": 40, "steps": steps, "terminated": True, + "termination_reason": "agent_done", "final_answer": answer, "success_self_report": True, + "verifier_path": f"sites/accuweather/verify/verify_{n}.py", "judge_rubric": ""} + (run_dir / "trajectory.json").write_text(json.dumps(traj, indent=2)) + + +def grade(n: int, run_dir: Path) -> dict: + r = subprocess.run([sys.executable, str(VERIFY_DIR / f"verify_{n}.py"), "--run_dir", str(run_dir), "--no_llm", "True"], capture_output=True, text=True) + try: + v = json.loads(r.stdout) + except json.JSONDecodeError: + v = {"pass": None, "reason": f"no JSON: {r.stderr[-300:]}"} + v["rc"] = r.returncode + return v + + +def main() -> None: + ap = argparse.ArgumentParser() + ap.add_argument("--site_dir", required=True); ap.add_argument("--python", required=True) + ap.add_argument("--port", type=int, default=45002); ap.add_argument("--out", required=True) + ap.add_argument("--tasks", default="") + a = ap.parse_args() + from playwright.sync_api import sync_playwright + site = Site(Path(a.site_dir).resolve(), a.python, a.port) + out = Path(a.out).resolve(); out.mkdir(parents=True, exist_ok=True) + tasks = [int(x) for x in a.tasks.split(",")] if a.tasks else sorted(CASES) + matrix = {} + seed_db = site.site_dir / "instance_seed" / "accuweather.db" + with sync_playwright() as p: + browser = p.chromium.launch() + for n in tasks: + case = CASES[n]; task_id = f"AccuWeather--{n}" + site.start() + try: + ctx = browser.new_context(viewport={"width": 1280, "height": 900}); page = ctx.new_page() + page.goto(site.base + "/", wait_until="networkidle") + run = out / f"task{n}_pass"; shutil.rmtree(run, ignore_errors=True); (run / "screenshots").mkdir(parents=True) + shutil.copy2(seed_db, run / "initial.db") + steps = drive(page, site.base, ACTIONS[n], run / "screenshots", case["answer"]) + ctx.close() + time.sleep(0.3) + shutil.copy2(site.site_dir / "instance" / "accuweather.db", run / "after.db") + finally: + site.stop() + write_traj(run, task_id, site.base, steps, case["answer"]) + row = {"pass": grade(n, run)} + # noop: homepage only, empty answer, clean DB + noop = out / f"task{n}_noop"; shutil.rmtree(noop, ignore_errors=True); (noop / "screenshots").mkdir(parents=True) + for i in (0, 1): + shutil.copy2(run / "screenshots" / "step_000.png", noop / "screenshots" / f"step_{i:03d}.png") + shutil.copy2(seed_db, noop / "initial.db"); shutil.copy2(seed_db, noop / "after.db") + write_traj(noop, task_id, site.base, [{"step": 0, "url": site.base + "/", "title": "AccuWeather", "thought": "", "action": "done", "params": {"text": "", "success": False}, "screenshot_before": "step_000.png", "screenshot_after": "step_001.png"}], "") + row["noop"] = grade(n, noop) + # shortcut: correct answer, no on-site navigation + sc = out / f"task{n}_shortcut"; shutil.rmtree(sc, ignore_errors=True); shutil.copytree(noop, sc) + write_traj(sc, task_id, site.base, [{"step": 0, "url": site.base + "/", "title": "AccuWeather", "thought": "", "action": "done", "params": {"text": case["answer"], "success": True}, "screenshot_before": "step_000.png", "screenshot_after": "step_001.png"}], case["answer"]) + row["shortcut"] = grade(n, sc) + # wrong answer(s) + row["wrong"] = [] + for k, (wrong_answer, _reason) in enumerate(case.get("wrong", [])): + w = out / f"task{n}_wrong{k}"; shutil.rmtree(w, ignore_errors=True); shutil.copytree(run, w) + write_traj(w, task_id, site.base, steps[:-1] + [dict(steps[-1], params={"text": wrong_answer, "success": True})], wrong_answer) + row["wrong"].append(grade(n, w)) + # state mismatch (stateful only): claims success, DB unchanged + if case.get("stateful"): + mm = out / f"task{n}_mismatch"; shutil.rmtree(mm, ignore_errors=True); shutil.copytree(run, mm) + shutil.copy2(seed_db, mm / "after.db") + row["mismatch"] = grade(n, mm) + matrix[n] = row + summary = {k: (v["pass"] if isinstance(v, dict) else [x["pass"] for x in v]) for k, v in row.items()} + reasons = {k: v.get("reason") for k, v in row.items() if isinstance(v, dict)} + print(f"task {n:2d}: {summary} reasons={reasons}", flush=True) + browser.close() + (out / "matrix.json").write_text(json.dumps(matrix, indent=1)) + ok = all(r["pass"]["pass"] is True and r["noop"]["pass"] is False and r["shortcut"]["pass"] is False + and all(w["pass"] is False for w in r["wrong"]) and r.get("mismatch", {"pass": False})["pass"] is False for r in matrix.values()) + print("MATRIX", "OK" if ok else "HAS FAILURES") + sys.exit(0 if ok else 1) + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/tests/test_verify_0.py b/sites/accuweather/verify/tests/test_verify_0.py new file mode 100644 index 000000000..5e2d3380a --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_0.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[0] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask0Tests(VerifierTestCase): + N = 0 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_1.py b/sites/accuweather/verify/tests/test_verify_1.py new file mode 100644 index 000000000..f6cf70feb --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_1.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[1] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask1Tests(VerifierTestCase): + N = 1 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_10.py b/sites/accuweather/verify/tests/test_verify_10.py new file mode 100644 index 000000000..ba4fa90a4 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_10.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[10] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask10Tests(VerifierTestCase): + N = 10 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_11.py b/sites/accuweather/verify/tests/test_verify_11.py new file mode 100644 index 000000000..7cfc05245 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_11.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[11] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask11Tests(VerifierTestCase): + N = 11 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_12.py b/sites/accuweather/verify/tests/test_verify_12.py new file mode 100644 index 000000000..8e6a44bda --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_12.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[12] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask12Tests(VerifierTestCase): + N = 12 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_13.py b/sites/accuweather/verify/tests/test_verify_13.py new file mode 100644 index 000000000..5481e9435 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_13.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[13] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask13Tests(VerifierTestCase): + N = 13 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_14.py b/sites/accuweather/verify/tests/test_verify_14.py new file mode 100644 index 000000000..964e3d66a --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_14.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[14] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask14Tests(VerifierTestCase): + N = 14 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_15.py b/sites/accuweather/verify/tests/test_verify_15.py new file mode 100644 index 000000000..491a367d6 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_15.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[15] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask15Tests(VerifierTestCase): + N = 15 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_state_mismatch_fails(self) -> None: + # agent self-reports success but the database is still the seed + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=State()), C["mismatch"]) + + def test_collateral_write_fails(self) -> None: + after = genuine_after() + after.add_saved("carol.w@test.com", "denver-co") + v = self.verdict(C["steps"], C["answer"], after=after) + self.assertFalse(v["pass"], "collateral saved_location write must fail") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_16.py b/sites/accuweather/verify/tests/test_verify_16.py new file mode 100644 index 000000000..e0205accf --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_16.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[16] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask16Tests(VerifierTestCase): + N = 16 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_17.py b/sites/accuweather/verify/tests/test_verify_17.py new file mode 100644 index 000000000..7c7fb0dc9 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_17.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[17] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask17Tests(VerifierTestCase): + N = 17 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_18.py b/sites/accuweather/verify/tests/test_verify_18.py new file mode 100644 index 000000000..2d9a88272 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_18.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[18] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask18Tests(VerifierTestCase): + N = 18 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_state_mismatch_fails(self) -> None: + # agent self-reports success but the database is still the seed + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=State()), C["mismatch"]) + + def test_collateral_write_fails(self) -> None: + after = genuine_after() + after.add_saved("carol.w@test.com", "denver-co") + v = self.verdict(C["steps"], C["answer"], after=after) + self.assertFalse(v["pass"], "collateral saved_location write must fail") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_19.py b/sites/accuweather/verify/tests/test_verify_19.py new file mode 100644 index 000000000..0f943d98a --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_19.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[19] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask19Tests(VerifierTestCase): + N = 19 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_2.py b/sites/accuweather/verify/tests/test_verify_2.py new file mode 100644 index 000000000..a6ec1ed37 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_2.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[2] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask2Tests(VerifierTestCase): + N = 2 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_3.py b/sites/accuweather/verify/tests/test_verify_3.py new file mode 100644 index 000000000..b4ba31449 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_3.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[3] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask3Tests(VerifierTestCase): + N = 3 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_4.py b/sites/accuweather/verify/tests/test_verify_4.py new file mode 100644 index 000000000..2f2d5a07e --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_4.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[4] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask4Tests(VerifierTestCase): + N = 4 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_5.py b/sites/accuweather/verify/tests/test_verify_5.py new file mode 100644 index 000000000..792a7787d --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_5.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[5] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask5Tests(VerifierTestCase): + N = 5 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_read_only_write_fails(self) -> None: + after = State() + after.add_saved("bob.smith@test.com", "seattle-wa") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "read_only_saved_location_unchanged") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_6.py b/sites/accuweather/verify/tests/test_verify_6.py new file mode 100644 index 000000000..2d000cd21 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_6.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[6] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask6Tests(VerifierTestCase): + N = 6 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_state_mismatch_fails(self) -> None: + # agent self-reports success but the database is still the seed + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=State()), C["mismatch"]) + + def test_collateral_write_fails(self) -> None: + after = genuine_after() + after.add_saved("carol.w@test.com", "denver-co") + v = self.verdict(C["steps"], C["answer"], after=after) + self.assertFalse(v["pass"], "collateral saved_location write must fail") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_7.py b/sites/accuweather/verify/tests/test_verify_7.py new file mode 100644 index 000000000..e88f44331 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_7.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[7] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask7Tests(VerifierTestCase): + N = 7 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_state_mismatch_fails(self) -> None: + # agent self-reports success but the database is still the seed + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=State()), C["mismatch"]) + + def test_collateral_write_fails(self) -> None: + after = genuine_after() + after.add_saved("carol.w@test.com", "denver-co") + v = self.verdict(C["steps"], C["answer"], after=after) + self.assertFalse(v["pass"], "collateral saved_location write must fail") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_8.py b/sites/accuweather/verify/tests/test_verify_8.py new file mode 100644 index 000000000..7e05770f0 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_8.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[8] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask8Tests(VerifierTestCase): + N = 8 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_state_mismatch_fails(self) -> None: + # agent self-reports success but the database is still the seed + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=State()), C["mismatch"]) + + def test_collateral_write_fails(self) -> None: + after = genuine_after() + after.add_saved("carol.w@test.com", "denver-co") + v = self.verdict(C["steps"], C["answer"], after=after) + self.assertFalse(v["pass"], "collateral saved_location write must fail") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_9.py b/sites/accuweather/verify/tests/test_verify_9.py new file mode 100644 index 000000000..0d4727f01 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_9.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from _cases import CASES # noqa: E402 +from _support import State, VerifierTestCase, step # noqa: E402 + +C = CASES[9] + + +def genuine_after() -> State: + after = State() + if C.get("after"): + C["after"](after) + return after + + +class VerifyTask9Tests(VerifierTestCase): + N = 9 + + def test_genuine_run_passes(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after())) + + def test_run_dir_snapshots_are_discovered(self) -> None: + self.assertPasses(self.verdict(C["steps"], C["answer"], after=genuine_after(), snapshots_in_run_dir=True)) + + def test_noop_run_fails_on_empty_answer(self) -> None: + self.assertFailsOn(self.verdict([step("/")], ""), "final_answer_nonempty") + + def test_other_task_trajectory_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), task_id="AccuWeather--99"), "trajectory_task_matches") + + def test_shortcut_fails_on_navigation_gate(self) -> None: + self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + + def test_wrong_answers_fail(self) -> None: + for answer, reason in C["wrong"]: + with self.subTest(answer=answer): + self.assertFailsOn(self.verdict(C["steps"], answer, after=genuine_after()), reason) + + def test_alternative_phrasings_pass(self) -> None: + for answer in C.get("also_pass", []): + with self.subTest(answer=answer): + self.assertPasses(self.verdict(C["steps"], answer, after=genuine_after())) + + def test_unterminated_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), updates={"terminated": False}), "trajectory_completed") + + def test_mixed_origin_run_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), start_url="http://127.0.0.1:41024/"), "all_urls_match_local_origin") + + def test_corrupt_screenshot_fails(self) -> None: + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + + def test_schema_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_catalog_change_fails_closed(self) -> None: + after = genuine_after() + after.extra_sql.append("UPDATE location SET temp = temp + 1 WHERE slug = 'phoenix-az'") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=after), "snapshot_contract_invalid") + + def test_wrong_seed_fails_closed(self) -> None: + initial = State() + initial.extra_sql.append("DELETE FROM saved_location WHERE id = 2") + self.assertFailsOn(self.verdict(C["steps"], C["answer"], initial=initial, after=genuine_after()), "snapshot_contract_invalid") + + def test_state_mismatch_fails(self) -> None: + # agent self-reports success but the database is still the seed + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=State()), C["mismatch"]) + + def test_collateral_write_fails(self) -> None: + after = genuine_after() + after.add_saved("carol.w@test.com", "denver-co") + v = self.verdict(C["steps"], C["answer"], after=after) + self.assertFalse(v["pass"], "collateral saved_location write must fail") + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/tests/test_verify_lib.py b/sites/accuweather/verify/tests/test_verify_lib.py new file mode 100644 index 000000000..7be236073 --- /dev/null +++ b/sites/accuweather/verify/tests/test_verify_lib.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +import sqlite3 +import sys +import tempfile +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + +import verify_lib as L # noqa: E402 +from _support import SITE_DIR, State, build_seed, scrypt_hash # noqa: E402 + + +class FixtureMatchesFrozenSeed(unittest.TestCase): + def test_fixture_catalog_fingerprint_equals_pinned_constant(self) -> None: + with tempfile.TemporaryDirectory() as d: + db = build_seed(Path(d) / "seed.db") + self.assertEqual(L.catalog_fingerprint(db), L.CATALOG_FINGERPRINT) + L._validate_snapshot_contract(str(db), str(db)) + + def test_real_seed_if_present_matches_fixture(self) -> None: + real = SITE_DIR / "instance_seed" / "accuweather.db" + if not real.is_file(): + self.skipTest("instance_seed/accuweather.db not generated in this checkout") + self.assertEqual(L.catalog_fingerprint(real), L.CATALOG_FINGERPRINT) + + +class FactMatchers(unittest.TestCase): + def test_contains_fact_unit_and_label(self) -> None: + self.assertTrue(L.contains_fact("temperature 104°F", 104, unit="temp", label=L.TEMP_LABEL)) + self.assertTrue(L.contains_fact("104° and RealFeel 115°", 104, unit="temp", label=L.TEMP_LABEL)) + self.assertTrue(L.contains_fact("RealFeel® temperature of 115°", 115, unit="temp", label=L.REALFEEL_LABEL)) + self.assertTrue(L.contains_fact("RealFeel® temperature of 115°", 104, unit="temp", label=L.TEMP_LABEL) is False) + self.assertFalse(L.contains_fact("temperature 115, RealFeel 104", 104, unit="temp", label=L.TEMP_LABEL)) + self.assertTrue(L.contains_fact("18% humidity, RealFeel 115", 18, unit="percent", label=L.HUMIDITY_LABEL, allow_bare=False)) + self.assertFalse(L.contains_fact("humidity 181%", 18, unit="percent", label=L.HUMIDITY_LABEL, allow_bare=False)) + self.assertFalse(L.contains_fact("humidity is not 18%", 18, unit="percent", label=L.HUMIDITY_LABEL)) + self.assertTrue(L.contains_fact("pressure 29.89 in", "29.89", unit="inhg", label=L.PRESSURE_LABEL)) + self.assertFalse(L.contains_fact("pressure 29.8 in", "29.89", unit="inhg", label=L.PRESSURE_LABEL)) + self.assertTrue(L.contains_fact("Austin's RealFeel is 103°", 103, unit="temp", label=L.city_label("austin"))) + self.assertFalse(L.contains_fact("Austin 82°, Denver 103°", 103, unit="temp", label=L.city_label("austin"))) + + def test_conditions_days_and_times(self) -> None: + self.assertTrue(L.contains_condition("it is mostly cloudy", "Mostly cloudy")) + self.assertFalse(L.contains_condition("Mostly cloudy", "Cloudy")) + self.assertTrue(L.contains_condition("Cloudy skies", "Cloudy")) + self.assertFalse(L.contains_condition("partly sunny", "Sunny")) + self.assertTrue(L.contains_day_label("Saturday", "Sat") and L.contains_day_label("Sat.", "Sat")) + self.assertFalse(L.contains_day_label("Sunday", "Sat")) + self.assertTrue(L.contains_clock_time("at 16:00", "4 PM") and L.contains_clock_time("4:00 p.m.", "4 PM")) + self.assertFalse(L.contains_clock_time("4 AM", "4 PM")) + + def test_names_winner(self) -> None: + w, l = ["denver"], ["austin"] + kw, inv = ["cooler", "lower"], ["warmer", "hotter"] + for good in ["Denver feels cooler than Austin", "Austin 103, Denver 82; Denver feels cooler", + "The cooler city is Denver", "Denver (82°) is cooler", "Austin is warmer than Denver", + "Austin 103 and Denver 82, so Denver feels cooler", "cooler: Denver"]: + self.assertTrue(L.names_winner(good, w, l, kw, inv), good) + for bad in ["Austin feels cooler than Denver", "The cooler city is Austin", "Denver is warmer", + "Austin 103, Denver 82", "Denver feels cooler. Austin feels cooler too."]: + self.assertFalse(L.names_winner(bad, w, l, kw, inv), bad) + + def test_search_surfaces_and_origin(self) -> None: + traj = {"start_url": "http://localhost:41024/", "steps": [{"url": "http://localhost:41024/search?q=Springfield+Missouri"}]} + self.assertTrue(L.search_surfaces(traj, "springfield-mo")) + self.assertTrue(L.search_surfaces(traj, "springfield-il")) + self.assertFalse(L.search_surfaces(traj, "phoenix-az")) + self.assertTrue(L.navigated_to_path({"steps": [{"url": "http://127.0.0.1:5000/weather/phoenix-az?x=1"}]}, "/weather/phoenix-az")) + self.assertFalse(L.navigated_to_path({"steps": [{"url": "http://example.com/weather/phoenix-az"}]}, "/weather/phoenix-az")) + self.assertFalse(L._same_local_origin("http://localhost:41025/", "http://localhost:41024/")) + + def test_password_matches(self) -> None: + self.assertTrue(L.password_matches(scrypt_hash("Weather123!"), "Weather123!")) + self.assertFalse(L.password_matches(scrypt_hash("Weather123!"), "weather123!")) + self.assertFalse(L.password_matches("garbage", "x")) + + +if __name__ == "__main__": + unittest.main() diff --git a/sites/accuweather/verify/verify_0.py b/sites/accuweather/verify/verify_0.py new file mode 100644 index 000000000..5cf2c684f --- /dev/null +++ b/sites/accuweather/verify/verify_0.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--0: Phoenix current temperature, RealFeel and humidity (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--0" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_visited_path(judge, t, "visited_weather_phoenix", "/weather/phoenix-az") + judge.check("answer_temperature", contains_fact(a, 104, unit="temp", label=TEMP_LABEL), f"expected 104, answer={a!r}") + judge.check("answer_realfeel", contains_fact(a, 115, unit="temp", label=REALFEEL_LABEL), f"expected 115, answer={a!r}") + judge.check("answer_humidity", contains_fact(a, 18, unit="percent", label=HUMIDITY_LABEL, allow_bare=False), f"expected 18%, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_1.py b/sites/accuweather/verify/verify_1.py new file mode 100644 index 000000000..ca9e17936 --- /dev/null +++ b/sites/accuweather/verify/verify_1.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--1: Portland search -> Portland, Maine condition, wind and visibility (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--1" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "portland-me") + check_visited_path(judge, t, "visited_weather_portland_me", "/weather/portland-me") + judge.check("answer_condition", contains_condition(a, "Mostly cloudy"), f"expected 'Mostly cloudy', answer={a!r}") + judge.check("answer_wind", contains_fact(a, 8, unit="mph", label=WIND_LABEL), f"expected 8 mph, answer={a!r}") + judge.check("answer_visibility", contains_fact(a, 10, unit="mi", label=VISIBILITY_LABEL), f"expected 10 mi, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_10.py b/sites/accuweather/verify/verify_10.py new file mode 100644 index 000000000..90b0c1610 --- /dev/null +++ b/sites/accuweather/verify/verify_10.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--10: Postal-code search 94102 -> San Francisco condition, humidity and pressure (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--10" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + judge.check("searched_postal_code_94102", any("94102" in normalize_text(q) for q in search_queries(t)), f"observed_queries={search_queries(t)!r}") + check_visited_path(judge, t, "visited_weather_san_francisco", "/weather/san-francisco-ca") + judge.check("answer_condition", contains_condition(a, "Cloudy"), f"expected 'Cloudy', answer={a!r}") + judge.check("answer_humidity", contains_fact(a, 75, unit="percent", label=HUMIDITY_LABEL, allow_bare=False), f"expected 75%, answer={a!r}") + judge.check("answer_pressure", contains_fact(a, "30.05", unit="inhg", label=PRESSURE_LABEL), f"expected 30.05 in, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_11.py b/sites/accuweather/verify/verify_11.py new file mode 100644 index 000000000..811df5d56 --- /dev/null +++ b/sites/accuweather/verify/verify_11.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--11: New Orleans radar page, then current condition (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--11" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "new-orleans-la") + check_visited_path(judge, t, "visited_radar_new_orleans", "/radar/new-orleans-la") + check_visited_path(judge, t, "visited_weather_new_orleans", "/weather/new-orleans-la") + judge.check("answer_condition", contains_condition(a, "Showers"), f"expected 'Showers', answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_12.py b/sites/accuweather/verify/verify_12.py new file mode 100644 index 000000000..aba104300 --- /dev/null +++ b/sites/accuweather/verify/verify_12.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--12: Los Angeles vs San Francisco air-quality comparison (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--12" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "los-angeles-ca") + check_search_surfaces(judge, t, "san-francisco-ca") + check_visited_path(judge, t, "visited_air_quality_los_angeles", "/air-quality/los-angeles-ca") + check_visited_path(judge, t, "visited_air_quality_san_francisco", "/air-quality/san-francisco-ca") + judge.check("answer_los_angeles_value", contains_fact(a, 41, label=city_label("los angeles", "la", "l.a.")), f"expected LA 41, answer={a!r}") + judge.check("answer_san_francisco_value", contains_fact(a, 18, label=city_label("san francisco", "sf")), f"expected SF 18, answer={a!r}") + judge.check("answer_names_better_city", names_winner(a, ["san francisco", "sf"], ["los angeles", "la", "l.a."], ["better", "lower", "cleaner", "best", "lowest", "healthier"], ["worse", "higher", "worst", "highest", "poorer"]), f"expected San Francisco better, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_13.py b/sites/accuweather/verify/verify_13.py new file mode 100644 index 000000000..9e013fec9 --- /dev/null +++ b/sites/accuweather/verify/verify_13.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--13: Boston daily: day with the lowest overnight low (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--13" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_visited_path(judge, t, "visited_daily_boston", "/daily/boston-ma") + judge.check("answer_day", contains_day_label(a, "Sat"), f"expected Sat, answer={a!r}") + judge.check("answer_low", contains_fact(a, 60, unit="temp", label=LOW_LABEL), f"expected low 60, answer={a!r}") + judge.check("answer_high", contains_fact(a, 73, unit="temp", label=HIGH_LABEL), f"expected high 73, answer={a!r}") + judge.check("answer_condition", contains_condition(a, "Mostly cloudy"), f"expected 'Mostly cloudy', answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_14.py b/sites/accuweather/verify/verify_14.py new file mode 100644 index 000000000..3408ad8c3 --- /dev/null +++ b/sites/accuweather/verify/verify_14.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--14: London current temperature, wind, humidity and air-quality category (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--14" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "london-gb") + check_visited_path(judge, t, "visited_weather_london", "/weather/london-gb") + check_visited_path(judge, t, "visited_air_quality_london", "/air-quality/london-gb") + judge.check("answer_temperature", contains_fact(a, 63, unit="temp", label=TEMP_LABEL), f"expected 63, answer={a!r}") + judge.check("answer_wind", contains_fact(a, 10, unit="mph", label=WIND_LABEL), f"expected 10 mph, answer={a!r}") + judge.check("answer_humidity", contains_fact(a, 72, unit="percent", label=HUMIDITY_LABEL, allow_bare=False), f"expected 72%, answer={a!r}") + judge.check("answer_air_quality_category", contains_all(a, ["good"]) and not contains_all(a, ["moderate"]), f"expected category 'Good', answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_15.py b/sites/accuweather/verify/verify_15.py new file mode 100644 index 000000000..5ee1ff908 --- /dev/null +++ b/sites/accuweather/verify/verify_15.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--15: Sign in as david.b@test.com and save Phoenix and Miami (stateful). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--15" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_signed_in_as(judge, t, "david.b@test.com") + check_paths_in_order(judge, t, "login_then_phoenix_then_account", ["/login", "/weather/phoenix-az", "/account"]) + check_paths_in_order(judge, t, "login_then_miami_then_account", ["/login", "/weather/miami-fl", "/account"]) + check_saved_delta(judge, initial_db, after_db, "david.b@test.com", added={"phoenix-az", "miami-fl"}, removed=set()) + judge.check("answer_mentions_both_cities", mentions(a, ["phoenix"]) and mentions(a, ["miami"]), f"answer={a!r}") + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_16.py b/sites/accuweather/verify/verify_16.py new file mode 100644 index 000000000..e958e43c2 --- /dev/null +++ b/sites/accuweather/verify/verify_16.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--16: Portland, Oregon vs Portland, Maine current temperature comparison (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--16" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "portland-or") + check_visited_path(judge, t, "visited_weather_portland_or", "/weather/portland-or") + check_visited_path(judge, t, "visited_weather_portland_me", "/weather/portland-me") + judge.check("answer_oregon_temperature", contains_fact(a, 69, unit="temp", label=city_label("oregon", "portland, or", "portland or", "portland, oregon")), f"expected Oregon 69, answer={a!r}") + judge.check("answer_maine_temperature", contains_fact(a, 70, unit="temp", label=city_label("maine", "portland, me", "portland me", "portland, maine")), f"expected Maine 70, answer={a!r}") + judge.check("answer_names_warmer_city", names_winner(a, ["maine", "portland, me", "portland, maine"], ["oregon", "portland, or", "portland, oregon"], ["warmer", "hotter", "higher", "warmest", "hottest"], ["cooler", "colder", "lower", "coolest"]), f"expected Maine warmer, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_17.py b/sites/accuweather/verify/verify_17.py new file mode 100644 index 000000000..836f5da75 --- /dev/null +++ b/sites/accuweather/verify/verify_17.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--17: Toronto hourly: first hour at the highest temperature and its precipitation chance (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--17" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "toronto-ca") + check_visited_path(judge, t, "visited_hourly_toronto", "/hourly/toronto-ca") + judge.check("answer_hour", contains_clock_time(a, "4 PM"), f"expected 4 PM, answer={a!r}") + judge.check("answer_temperature", contains_fact(a, 73, unit="temp", label=TEMP_LABEL), f"expected 73, answer={a!r}") + judge.check("answer_precipitation", contains_fact(a, 64, unit="percent", label=PRECIP_LABEL, allow_bare=False), f"expected 64%, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_18.py b/sites/accuweather/verify/verify_18.py new file mode 100644 index 000000000..16dedb600 --- /dev/null +++ b/sites/accuweather/verify/verify_18.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--18: Register Jamie Lee (jamie.lee@example.test / Weather123!) and save Atlanta (stateful). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--18" + +NEW_EMAIL = "jamie.lee@example.test" + + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + judge.check("visited_register_page", check_visited_path(judge, t, "visited_register", "/register"), "required_path=/register") + judge.check("entered_new_account_email", normalize_text(NEW_EMAIL) in typed_emails(t), f"typed_emails={typed_emails(t)!r}") + check_paths_in_order(judge, t, "register_then_atlanta_then_account", ["/register", "/weather/atlanta-ga", "/account"]) + fresh = new_users(initial_db, after_db) + judge.check("exactly_one_new_user", len(fresh) == 1, f"new_users={[u['email'] for u in fresh]}") + u = fresh[0] if len(fresh) == 1 else None + judge.check("new_user_email", bool(u) and normalize_text(u["email"]) == NEW_EMAIL, f"observed={u and u['email']!r}") + judge.check("new_user_name", bool(u) and normalize_text(u["name"]) == "jamie lee", f"observed={u and u['name']!r}") + judge.check("new_user_password_verifies", bool(u) and password_matches(u["password_hash"], "Weather123!"), "werkzeug hash checked with hashlib") + judge.check("seeded_users_unchanged", rows_unchanged_except(initial_db, after_db, "user", [u["id"]] if u else []), "seeded user rows identical") + before, after = saved_pairs(initial_db), saved_pairs(after_db) + judge.check("saved_rows_added", (after - before) == {(NEW_EMAIL, "atlanta-ga")}, f"observed_added={sorted(after - before)}") + judge.check("saved_rows_removed", (before - after) == set(), f"observed_removed={sorted(before - after)}") + check_tables_unchanged(judge, initial_db, after_db, ("alert",)) + judge.check("answer_mentions_atlanta", mentions(a, ["atlanta"]), f"answer={a!r}") + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_19.py b/sites/accuweather/verify/verify_19.py new file mode 100644 index 000000000..70d3bb7fc --- /dev/null +++ b/sites/accuweather/verify/verify_19.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--19: Phoenix vs New Orleans temperature/humidity and RealFeel heat-index comparison (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--19" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "new-orleans-la") + check_visited_path(judge, t, "visited_weather_phoenix", "/weather/phoenix-az") + check_visited_path(judge, t, "visited_weather_new_orleans", "/weather/new-orleans-la") + judge.check("answer_phoenix_temperature", contains_fact(a, 104, unit="temp", label=TEMP_LABEL), f"expected 104, answer={a!r}") + judge.check("answer_phoenix_humidity", contains_fact(a, 18, unit="percent", label=HUMIDITY_LABEL, allow_bare=False), f"expected 18%, answer={a!r}") + judge.check("answer_new_orleans_temperature", contains_fact(a, 89, unit="temp", label=TEMP_LABEL), f"expected 89, answer={a!r}") + judge.check("answer_new_orleans_humidity", contains_fact(a, 75, unit="percent", label=HUMIDITY_LABEL, allow_bare=False), f"expected 75%, answer={a!r}") + judge.check("answer_names_higher_heat_index_city", names_winner(a, ["phoenix"], ["new orleans", "nola"], ["higher", "hotter", "greater", "highest", "hottest", "larger"], ["lower", "cooler", "lowest", "smaller", "less"]), f"expected Phoenix higher, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_2.py b/sites/accuweather/verify/verify_2.py new file mode 100644 index 000000000..4f82d9afe --- /dev/null +++ b/sites/accuweather/verify/verify_2.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--2: Seattle hourly: first hour with >= 40% precipitation (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--2" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_visited_path(judge, t, "visited_hourly_seattle", "/hourly/seattle-wa") + judge.check("answer_hour", contains_clock_time(a, "4 PM"), f"expected 4 PM, answer={a!r}") + judge.check("answer_temperature", contains_fact(a, 69, unit="temp", label=TEMP_LABEL), f"expected 69, answer={a!r}") + judge.check("answer_condition", contains_condition(a, "Showers"), f"expected 'Showers', answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_3.py b/sites/accuweather/verify/verify_3.py new file mode 100644 index 000000000..1bac8cd42 --- /dev/null +++ b/sites/accuweather/verify/verify_3.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--3: Miami daily: day with the greatest precipitation chance (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--3" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_visited_path(judge, t, "visited_daily_miami", "/daily/miami-fl") + judge.check("answer_day", contains_day_label(a, "Sat"), f"expected Sat, answer={a!r}") + judge.check("answer_high", contains_fact(a, 86, unit="temp", label=HIGH_LABEL), f"expected high 86, answer={a!r}") + judge.check("answer_low", contains_fact(a, 73, unit="temp", label=LOW_LABEL), f"expected low 73, answer={a!r}") + judge.check("answer_condition", contains_condition(a, "Mostly cloudy"), f"expected 'Mostly cloudy', answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_4.py b/sites/accuweather/verify/verify_4.py new file mode 100644 index 000000000..d08c20120 --- /dev/null +++ b/sites/accuweather/verify/verify_4.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--4: Austin vs Denver RealFeel comparison (read-only). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--4" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_visited_path(judge, t, "visited_weather_austin", "/weather/austin-tx") + check_visited_path(judge, t, "visited_weather_denver", "/weather/denver-co") + judge.check("answer_austin_realfeel", contains_fact(a, 103, unit="temp", label=city_label("austin", "austin, tx", "austin, texas")), f"expected Austin 103, answer={a!r}") + judge.check("answer_denver_realfeel", contains_fact(a, 82, unit="temp", label=city_label("denver", "denver, co", "denver, colorado")), f"expected Denver 82, answer={a!r}") + judge.check("answer_names_cooler_city", names_winner(a, ["denver"], ["austin"], ["cooler", "colder", "lower", "less hot", "coolest"], ["warmer", "hotter", "higher", "warmest", "hottest"]), f"expected Denver cooler, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_5.py b/sites/accuweather/verify/verify_5.py new file mode 100644 index 000000000..5b767b526 --- /dev/null +++ b/sites/accuweather/verify/verify_5.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--5: Springfield search -> Missouri: postal code, pressure and air-quality value (read-only; task re-anchored by the reviewer). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--5" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_search_surfaces(judge, t, "springfield-mo") + check_visited_path(judge, t, "visited_weather_springfield_mo", "/weather/springfield-mo") + check_visited_path(judge, t, "visited_air_quality_springfield_mo", "/air-quality/springfield-mo") + judge.check("answer_postal_code", contains_all(a, ["65806"]), f"expected 65806, answer={a!r}") + judge.check("answer_pressure", contains_fact(a, "29.89", unit="inhg", label=PRESSURE_LABEL), f"expected 29.89 in, answer={a!r}") + judge.check("answer_air_quality", contains_fact(a, 39, label=AQ_LABEL), f"expected 39, answer={a!r}") + check_read_only(judge, initial_db, after_db) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_6.py b/sites/accuweather/verify/verify_6.py new file mode 100644 index 000000000..e1e95ae79 --- /dev/null +++ b/sites/accuweather/verify/verify_6.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--6: Sign in as alice.j@test.com and save Seattle (stateful). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--6" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_signed_in_as(judge, t, "alice.j@test.com") + check_paths_in_order(judge, t, "login_then_seattle_then_account", ["/login", "/weather/seattle-wa", "/account"]) + check_saved_delta(judge, initial_db, after_db, "alice.j@test.com", added={"seattle-wa"}, removed=set()) + judge.check("answer_mentions_seattle", mentions(a, ["seattle"]), f"answer={a!r}") + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_7.py b/sites/accuweather/verify/verify_7.py new file mode 100644 index 000000000..32ef6872f --- /dev/null +++ b/sites/accuweather/verify/verify_7.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--7: Sign in as alice.j@test.com and remove Boston from Saved Locations (stateful). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--7" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_signed_in_as(judge, t, "alice.j@test.com") + check_paths_in_order(judge, t, "login_then_boston_then_account", ["/login", "/weather/boston-ma", "/account"]) + check_saved_delta(judge, initial_db, after_db, "alice.j@test.com", added=set(), removed={"boston-ma"}) + judge.check("answer_mentions_boston", mentions(a, ["boston"]), f"answer={a!r}") + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_8.py b/sites/accuweather/verify/verify_8.py new file mode 100644 index 000000000..022caefd1 --- /dev/null +++ b/sites/accuweather/verify/verify_8.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--8: Sign in as bob.smith@test.com and enable exactly Severe weather + Rain starting soon for Chicago (stateful). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--8" + +EXPECTED_ALERTS = {("bob.smith@test.com", "chicago-il", "severe", 1), ("bob.smith@test.com", "chicago-il", "rain", 1)} + + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + check_signed_in_as(judge, t, "bob.smith@test.com") + check_paths_in_order(judge, t, "login_then_chicago_alerts", ["/login", "/alerts/chicago-il"]) + before, after = alert_rows(initial_db), alert_rows(after_db) + judge.check("alerts_initially_empty", before == set(), f"initial_alerts={sorted(before)}") + judge.check("alerts_exactly_severe_and_rain_for_chicago", after == EXPECTED_ALERTS, f"expected={sorted(EXPECTED_ALERTS)}, observed={sorted(after)}") + check_tables_unchanged(judge, initial_db, after_db, ("user", "saved_location")) + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_9.py b/sites/accuweather/verify/verify_9.py new file mode 100644 index 000000000..a194b9c7a --- /dev/null +++ b/sites/accuweather/verify/verify_9.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Verify AccuWeather--9: Sign in as carol.w@test.com, switch to Celsius, report New York temperature and RealFeel in Celsius (stateful). + +Deterministic only (no LLM calls). Ground truth is hardcoded below and never +appears in tasks.jsonl. Order: package identity -> navigation gates -> answer +facts -> SQLite snapshot contract. +""" +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from verify_lib import ( # noqa: E402 + AQ_LABEL, HIGH_LABEL, HUMIDITY_LABEL, LOW_LABEL, PRECIP_LABEL, PRESSURE_LABEL, REALFEEL_LABEL, + TEMP_LABEL, VISIBILITY_LABEL, WIND_LABEL, Judge, alert_rows, check_paths_in_order, check_read_only, + check_saved_delta, check_search_surfaces, check_signed_in_as, check_tables_unchanged, + check_trajectory_identity, check_visited_path, city_label, contains_all, contains_any, + contains_clock_time, contains_condition, contains_day_label, contains_fact, fail_closed, + final_answer, load_run, mentions, names_winner, new_users, normalize_text, parse_args, + password_matches, resolve_snapshots, rows_unchanged_except, saved_pairs, search_queries, + table_rows, typed_emails, user_by_email, +) + +TASK_ID = "AccuWeather--9" + +def run_checks(judge, t, initial_db, after_db): + check_trajectory_identity(judge, t, TASK_ID) + a = final_answer(t) + check_signed_in_as(judge, t, "carol.w@test.com") + check_paths_in_order(judge, t, "login_then_settings_then_new_york", ["/login", "/settings", "/weather/new-york-ny"]) + before, after = user_by_email(initial_db, "carol.w@test.com"), user_by_email(after_db, "carol.w@test.com") + judge.check("carol_unit_is_celsius", bool(after) and after["unit"] == "C", f"before_unit={before and before['unit']!r}, after_unit={after and after['unit']!r}") + judge.check("carol_other_columns_unchanged", bool(before and after) and {k: v for k, v in before.items() if k != "unit"} == {k: v for k, v in after.items() if k != "unit"}, "email/name/password_hash identical") + judge.check("other_users_unchanged", bool(before) and rows_unchanged_except(initial_db, after_db, "user", [before["id"]]), "user rows other than carol identical") + check_tables_unchanged(judge, initial_db, after_db, ("saved_location", "alert")) + judge.check("answer_temperature_celsius", contains_fact(a, 26, unit="temp", label=TEMP_LABEL), f"expected 26, answer={a!r}") + judge.check("answer_realfeel_celsius", contains_fact(a, 28, unit="temp", label=REALFEEL_LABEL), f"expected 28, answer={a!r}") + + +def main() -> None: + args = parse_args() + try: + trajectory = load_run(args.run_dir) + except (OSError, ValueError) as exc: + fail_closed(TASK_ID, "trajectory_unavailable", str(exc)) + initial_db, after_db = resolve_snapshots(args, TASK_ID) + judge = Judge(TASK_ID, args.no_llm) + try: + run_checks(judge, trajectory, initial_db, after_db) + except Exception as exc: # noqa: BLE001 - any verifier error fails closed + fail_closed(TASK_ID, "verifier_error", f"{type(exc).__name__}: {exc}") + judge.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/accuweather/verify/verify_lib.py b/sites/accuweather/verify/verify_lib.py new file mode 100644 index 000000000..a6f722d79 --- /dev/null +++ b/sites/accuweather/verify/verify_lib.py @@ -0,0 +1,918 @@ +#!/usr/bin/env python3 +"""verify_lib.py — shared deterministic utilities for AccuWeather task verification. + +Philosophy: DETERMINISTIC FIRST. Mirrors the sites/merriam_webster/verify API +(load_run / navigated_to / final_answer / contains_* / db helpers / Judge / +parse_args, plus llm_text_match / llm_screenshot_shows kept for parity) and adds +the sites/walmart_careers hardening: + + 1. Package identity: task_id matches, run terminated with ``agent_done``, + non-empty final answer, every recorded URL on the same loopback origin + (host AND port) as ``start_url``, every referenced screenshot is a + decodable PNG. + 2. Navigation gates (anti knowledge-shortcut): the agent MUST have opened the + on-site page(s) that render the requested facts; comparison tasks need + every detail page; when the only UI path to a location is the search box + the trajectory must contain a ``/search?q=`` visit that surfaces it. + 3. Answer checks: negation-aware token / number / percentage / clock-time / + day-label / winner matchers against ground truth HARDCODED in verify_N.py. + 4. SQLite snapshot contract: table set + columns + catalog fingerprint are + pinned; catalog tables are immutable; read-only tasks leave user / + saved_location / alert rows identical; stateful tasks must show exactly + the allowed row delta and nothing else. + 5. LLM utilities exist only for API parity with merriam_webster. No verdict + depends on them: the per-task verifiers never call them, and ``--no_llm`` + is accepted for CLI parity. + +Input signature (per task): + --run_dir DIR agent run: trajectory.json + screenshots/step_NNN.png + --initial_db PATH initial-state SQLite DB (default: /initial.db, + else docker cp of instance_seed from --container) + --after_db PATH after-state SQLite DB (default: /after.db, + else docker cp of the live instance from --container) + --container NAME docker container to fetch DBs from ($WH_CONTAINER / wh-review) + --no_llm [True] accepted for parity; verifiers are deterministic-only +Output: JSON {task_id, pass, reason, evidence[]} on stdout; exit 0 PASS / 1 FAIL. +""" + +import atexit +import base64 +import hashlib +import hmac +import ipaddress +import json +import os +import re +import sqlite3 +import struct +import subprocess +import sys +import tempfile +import unicodedata +import urllib.request +import zlib +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Iterable, Sequence +from urllib.parse import parse_qs, urlparse + +SITE = "accuweather" +DEFAULT_CONTAINER = os.environ.get("WH_CONTAINER", "wh-review") + +# Location identity (NOT answers): used to decide whether a /search?q= visit +# would surface a location under the site's own token-intersection scoring. +CATALOG = { + "new-york-ny": ("New York", "New York", "United States", "10007"), + "phoenix-az": ("Phoenix", "Arizona", "United States", "85001"), + "seattle-wa": ("Seattle", "Washington", "United States", "98101"), + "miami-fl": ("Miami", "Florida", "United States", "33101"), + "chicago-il": ("Chicago", "Illinois", "United States", "60601"), + "boston-ma": ("Boston", "Massachusetts", "United States", "02108"), + "austin-tx": ("Austin", "Texas", "United States", "78701"), + "denver-co": ("Denver", "Colorado", "United States", "80202"), + "portland-or": ("Portland", "Oregon", "United States", "97205"), + "portland-me": ("Portland", "Maine", "United States", "04101"), + "springfield-il": ("Springfield", "Illinois", "United States", "62701"), + "springfield-ma": ("Springfield", "Massachusetts", "United States", "01103"), + "springfield-mo": ("Springfield", "Missouri", "United States", "65806"), + "san-francisco-ca": ("San Francisco", "California", "United States", "94102"), + "los-angeles-ca": ("Los Angeles", "California", "United States", "90012"), + "atlanta-ga": ("Atlanta", "Georgia", "United States", "30303"), + "nashville-tn": ("Nashville", "Tennessee", "United States", "37219"), + "new-orleans-la": ("New Orleans", "Louisiana", "United States", "70112"), + "london-gb": ("London", "England", "United Kingdom", "SW1A"), + "toronto-ca": ("Toronto", "Ontario", "Canada", "M5H"), +} +# Cards linked from the homepage grid (Location.query.limit(8)); every other +# location is reachable through the UI only via the search box. +HOME_GRID_SLUGS = ("new-york-ny", "phoenix-az", "seattle-wa", "miami-fl", + "chicago-il", "boston-ma", "austin-tx", "denver-co") + +SEED_USERS = ("alice.j@test.com", "bob.smith@test.com", "carol.w@test.com", "david.b@test.com") +PASSWORD = "TestPass123!" + + +# ---------------------------------------------------------------- CLI +@dataclass +class VerifyArgs: + run_dir: str = "" + initial_db: str = "" + after_db: str = "" + container: str = DEFAULT_CONTAINER + no_llm: bool = False + + def post_process(self): + if not self.run_dir: + raise SystemExit("--run_dir is required") + + +def parse_args() -> VerifyArgs: + """simpleArgParser when available (eval_judge runs verifiers in agent_demo's + env, where ``--no_llm True`` is the convention); plain argparse otherwise.""" + try: + import simpleArgParser as sap # type: ignore + args = sap.parse_args(VerifyArgs) + except ImportError: + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("--run_dir", required=True) + parser.add_argument("--initial_db", default="") + parser.add_argument("--after_db", default="") + parser.add_argument("--container", default=DEFAULT_CONTAINER) + parser.add_argument("--no_llm", nargs="?", const="True", default="False") + ns = parser.parse_args() + args = VerifyArgs(ns.run_dir, ns.initial_db, ns.after_db, ns.container, + str(ns.no_llm).lower() in {"1", "true", "yes"}) + args.post_process() + run_dir = Path(args.run_dir) + if not args.initial_db and (run_dir / "initial.db").is_file(): + args.initial_db = str(run_dir / "initial.db") + if not args.after_db and (run_dir / "after.db").is_file(): + args.after_db = str(run_dir / "after.db") + return args + + +# ---------------------------------------------------------------- trajectory +def load_run(run_dir) -> dict[str, Any]: + d = Path(run_dir) + traj = json.loads((d / "trajectory.json").read_text(encoding="utf-8")) + if not isinstance(traj, dict): + raise ValueError("trajectory.json must contain a JSON object") + traj["_run_dir"] = d + traj["_shots"] = {p.name: p for p in sorted((d / "screenshots").glob("step_*.png"))} if (d / "screenshots").is_dir() else {} + return traj + + +def final_answer(traj) -> str: + return str(traj.get("final_answer") or "").strip() + + +def trajectory_urls(traj) -> list[str]: + urls: list[str] = [] + if traj.get("start_url"): + urls.append(str(traj["start_url"])) + for step in traj.get("steps") or []: + if not isinstance(step, dict): + continue + for key in ("url", "url_before", "url_after"): + if step.get(key): + urls.append(str(step[key])) + if traj.get("final_url"): + urls.append(str(traj["final_url"])) + return urls + + +def step_urls(traj) -> list[str]: # merriam_webster parity + return [str(s.get("url", "")) for s in traj.get("steps", []) if isinstance(s, dict)] + + +def normalized_url_path(url: str) -> str: + path = urlparse(str(url or "")).path or "/" + return path.rstrip("/") or "/" + + +def is_site_url(url: str) -> bool: + """HTTP(S) URL on a loopback host (any port: runs use alt ports).""" + parsed = urlparse(str(url or "")) + if parsed.scheme not in {"http", "https"} or not parsed.hostname: + return False + host = parsed.hostname.casefold() + if host == "localhost": + return True + try: + return ipaddress.ip_address(host).is_loopback + except ValueError: + return False + + +def site_urls(traj) -> list[str]: + return [u for u in trajectory_urls(traj) if is_site_url(u)] + + +def navigated_to(traj, substr: str, times: int = 1) -> bool: + """merriam_webster parity: at least `times` recorded site URLs contain substr.""" + return sum(1 for u in site_urls(traj) if substr in u) >= times + + +def navigated_any(traj, substrs: Iterable[str]) -> bool: + return any(navigated_to(traj, s) for s in substrs) + + +def navigated_to_path(traj, path: str) -> bool: + """Exact mirror path on a loopback origin (query string ignored).""" + expected = normalized_url_path(path) + return any(normalized_url_path(u) == expected for u in site_urls(traj)) + + +def search_queries(traj) -> list[str]: + out = [] + for u in site_urls(traj): + if normalized_url_path(u) == "/search": + out.extend(parse_qs(urlparse(u).query, keep_blank_values=True).get("q", [])) + return out + + +def _tokens(text: str) -> set[str]: + return set(re.findall(r"[a-z0-9]+", normalize_text(text))) + + +def search_surfaces(traj, slug: str) -> bool: + """True when some /search?q= visit would list `slug` under the site's own + scoring (any query token hits city / region / country / postal).""" + fields = " ".join(CATALOG[slug]) + field_tokens = _tokens(fields) + return any(_tokens(q) & field_tokens for q in search_queries(traj)) + + +def typed_texts(traj) -> list[str]: + values = [] + for step in traj.get("steps") or []: + if isinstance(step, dict) and normalize_text(step.get("action")) == "input": + params = step.get("params") + if isinstance(params, dict) and params.get("text") is not None: + values.append(str(params["text"])) + return values + + +def typed_emails(traj) -> list[str]: + return [normalize_text(v) for v in typed_texts(traj) + if re.fullmatch(r"[^@\s]+@[^@\s]+\.[^@\s]+", v.strip())] + + +def _shot(traj, name): + if not name: + return None + p = traj["_shots"].get(Path(str(name)).name) + return p if (p and p.exists()) else None + + +def shot_after_url(traj, substr): + for s in traj.get("steps", []): + if substr in str(s.get("url", "")): + p = _shot(traj, s.get("screenshot_after")) + if p: + return p + return None + + +def last_shot(traj): + for s in reversed(traj.get("steps", [])): + p = _shot(traj, s.get("screenshot_after")) or _shot(traj, s.get("screenshot_before")) + if p: + return p + shots = sorted(traj["_shots"].values()) + return shots[-1] if shots else None + + +def _png_dimensions(path: Path) -> tuple[int, int]: + data = path.read_bytes() + if len(data) < 33 or data[:8] != b"\x89PNG\r\n\x1a\n" or data[12:16] != b"IHDR": + raise ValueError("not a PNG") + width, height = struct.unpack(">II", data[16:24]) + if struct.unpack(">I", data[29:33])[0] != zlib.crc32(data[12:29]) & 0xFFFFFFFF: + raise ValueError("corrupt IHDR") + return width, height + + +def screenshots_decode(traj) -> tuple[bool, str]: + root = Path(traj.get("_run_dir") or "") + steps = traj.get("steps") + if not root.is_dir() or not isinstance(steps, list) or not steps: + return False, "run directory or steps are missing" + checked = 0 + for i, step in enumerate(steps): + if not isinstance(step, dict): + return False, f"step {i} is not an object" + for key in ("screenshot_before", "screenshot_after"): + name = step.get(key) + rel = Path(str(name or "")) + if not name or rel.is_absolute() or ".." in rel.parts: + return False, f"step {i} has unsafe {key}" + path = next((c for c in (root / "screenshots" / rel, root / rel) if c.is_file()), None) + if path is None: + return False, f"step {i} is missing {key}={name!r}" + try: + w, h = _png_dimensions(path) + except Exception as exc: # noqa: BLE001 + return False, f"step {i} {key} cannot decode: {exc}" + if w < 1 or h < 1: + return False, f"step {i} {key} is an empty PNG" + checked += 1 + return True, f"decoded {checked} PNG screenshots" + + +# ---------------------------------------------------------------- text matchers +def normalize_text(value: Any) -> str: + text = unicodedata.normalize("NFKC", str(value or "")) + text = text.replace("’", "'").replace("‘", "'").replace("“", '"').replace("”", '"') + text = text.replace("®", "").replace("™", "") + return re.sub(r"\s+", " ", text).strip().casefold() + + +def norm(s): # merriam_webster parity + return normalize_text(s) + + +_NEG_BEFORE = r"\b(?:not|no|never|without|wrong|incorrect|isn't|wasn't|isnt|wasnt|aren't|don't|doesn't|neither|nor)\b" +_NEG_AFTER = r"\s*(?:is|was|are|were)?\s*(?:not|wrong|incorrect)\b" + + +def _match_is_affirmative(text: str, match: re.Match) -> bool: + before = re.split(r"[.!?;:\n]+|\b(?:but|however|instead|whereas|while)\b", text[:match.start()], flags=re.I)[-1] + after = text[match.end():] + return not re.search(_NEG_BEFORE, before, re.I) and not re.match(_NEG_AFTER, after, re.I) + + +def _affirmative_search(pattern: str, text: str, flags: int = 0) -> bool: + return any(_match_is_affirmative(text, m) for m in re.finditer(pattern, text, flags)) + + +def answer_equals(final, expected) -> bool: + return normalize_text(final) == normalize_text(expected) + + +def contains_all(text, expected: Iterable[Any]) -> bool: + t = normalize_text(text) + return all(bool(v) and _affirmative_search(re.escape(v), t) for v in (normalize_text(e) for e in expected)) + + +def contains_any(text, expected: Iterable[Any]) -> bool: + t = normalize_text(text) + return any(bool(v) and _affirmative_search(re.escape(v), t) for v in (normalize_text(e) for e in expected)) + + +def _num_pattern(value) -> str: + """Regex for a standalone number: not part of a longer digit run, a decimal + or a thousands group (``104`` does not match ``1040`` or ``104.5``).""" + s = str(value) + if re.fullmatch(r"\d+\.\d+", s): + body = re.escape(s) + else: + body = rf"{int(s)}(?:\.0+)?" + return rf"(? bool: + return _affirmative_search(_num_pattern(value), normalize_text(text)) + + +_UNIT_PATTERNS = { + "temp": r"(?:°|º|˚|degrees?|deg\b|f\b|c\b|fahrenheit|celsius)", + "percent": r"(?:%|percent|pct\b|per cent)", + "mph": r"(?:mph|mi/h|miles? per hour|miles?/h|km/?h|kph)", + "mi": r"(?:mi\b|miles?\b|km\b)", + "inhg": r"(?:in\b|inhg|inches|\"|mb\b|hpa)", +} + + +def contains_measure(text, value, unit: str | None = None, label: str | None = None, + allow_bare: bool = False) -> bool: + """`value` immediately followed by a unit of kind `unit` (``104°``, ``104 F``, + ``18%``, ``8 mph``), OR preceded within the same clause by `label` + (``humidity: 18``, ``wind speed of 8``). `allow_bare` accepts a standalone + number with neither unit nor label.""" + t = normalize_text(text) + num = _num_pattern(value) + patterns = [] + if unit: + patterns.append(rf"{num}\s*(?:°\s*)?{_UNIT_PATTERNS[unit]}") + if label: + patterns.append(rf"(?:{label})[^.;\n]{{0,40}}?{num}") + if allow_bare or not patterns: + patterns.append(num) + return any(_affirmative_search(p, t) for p in patterns) + + +def contains_temperature(text, value, label: str | None = None) -> bool: + return contains_measure(text, value, unit="temp", label=label or r"temp(?:erature)?|realfeel|feels like|high|low|current") + + +def contains_percent(text, value, label: str | None = None) -> bool: + return contains_measure(text, value, unit="percent", label=label) + + +# Labels an agent typically writes next to a value. ``contains_fact`` uses them to +# catch value/label swaps (``temperature 115, RealFeel 104``) while staying lenient +# for unlabeled answers (``104°, 115°, 18%``). +TEMP_LABEL = r"(? str: + """Label regex for ``[’s] [metric]`` (``Austin's RealFeel 103``).""" + # longest alias first + trailing word boundary so ``portland, or`` never + # matches as a prefix of ``portland, oregon`` + alts = "|".join(re.escape(normalize_text(n)) for n in sorted(names, key=len, reverse=True)) + met = "|".join(re.escape(m) for m in sorted(metrics, key=len, reverse=True)) + return rf"\b(?:{alts})\b(?:'s)?(?:\s+(?:{met})\b)?" + + +def _num_norm(value) -> str: + s = str(value) + return s.rstrip("0").rstrip(".") if "." in s else s + + +def labeled_values(text, label: str) -> list[str]: + """Numbers bound to each occurrence of `label` by simple glue (``humidity: 18``, + ``RealFeel of 115°``). ``18% humidity, RealFeel 115`` binds nothing to + ``humidity`` because ``, realfeel`` is not glue.""" + t = normalize_text(text) + found = [] + for m in re.finditer(label, t): + n = re.match(_GLUE + rf"({_NUM_RE})", t[m.end():m.end() + 60]) + if n: + found.append(_num_norm(n.group(1))) + return found + + +def contains_fact(text, expected, unit: str | None = None, label: str | None = None, allow_bare: bool = True) -> bool: + """Ground-truth number check. If the answer binds a number to `label`, that + number must be `expected` (swap detection); otherwise fall back to unit + adjacency (``104°``) or, with `allow_bare`, a standalone number.""" + exp = _num_norm(expected) + if label: + bound = labeled_values(text, label) + if bound: + if not any(v == exp for v in bound): + return False + return contains_measure(text, expected, unit=unit, label=None, allow_bare=True) + return contains_measure(text, expected, unit=unit, label=None, allow_bare=allow_bare) + + +_MERIDIEM = {"am": r"a\.?\s*m\b\.?", "pm": r"p\.?\s*m\b\.?"} + + +def _clock_pattern(value: str) -> str: + m = re.fullmatch(r"\s*(\d{1,2})(?::(\d{2}))?\s*([AaPp])\.?\s*[Mm]\.?\s*", str(value)) + if not m: + raise ValueError(f"unsupported clock time: {value!r}") + hour12 = int(m.group(1)); minute = int(m.group(2) or 0); meridiem = "am" if m.group(3).lower() == "a" else "pm" + hour24 = hour12 % 12 + (12 if meridiem == "pm" else 0) + minutes = f":{minute:02d}" if minute else r"(?::00)?" + alts = [rf"(? bool: + return _affirmative_search(_clock_pattern(value), normalize_text(text)) + + +_DAYS = {"mon": "monday", "tue": "tuesday", "wed": "wednesday", "thu": "thursday", + "fri": "friday", "sat": "saturday", "sun": "sunday"} + + +def contains_day_label(text, label: str) -> bool: + """``Sat`` / ``Sat.`` / ``Saturday``; ``Today`` matches only the word today.""" + key = normalize_text(label)[:3] + if key == "tod": + return _affirmative_search(r"\btoday\b", normalize_text(text)) + full = _DAYS[key] + return _affirmative_search(rf"\b(?:{key}\.?|{full})\b", normalize_text(text)) + + +def contains_condition(text, condition: str) -> bool: + """``Mostly cloudy`` / ``mostly-cloudy`` / ``MOSTLY CLOUDY``.""" + words = normalize_text(condition).split() + pattern = r"\b" + r"[\s-]+".join(re.escape(w) for w in words) + r"\b" + if len(words) == 1: # ``Cloudy`` must not be satisfied by ``Mostly cloudy`` + pattern = r"(? bool: + return contains_any(text, terms) + + +def _term_pattern(terms: Sequence[str]) -> str: + return "(?:" + "|".join(r"\b" + re.escape(normalize_text(t)) + r"\b" for t in terms) + ")" + + +def _subject_near(sentence: str, kw: re.Match, a: Sequence[str], b: Sequence[str]) -> str | None: + """Which side (``'a'``/``'b'``) a comparative keyword refers to inside one + sentence. Rules, in order: (1) `` [(82°)] [feels|is] [slightly] ``; + (2) `` [one|city] [is|:] ``; (3) the nearest city before the + keyword; (4) the first city after it.""" + pa, pb = _term_pattern(a), _term_pattern(b) + glue = r"[\s,()°º\d.%f-]*(?:feels|is|has|was|reads|shows|reports|comes in|ranks|remains)?\s*(?:the|a|slightly|much|clearly|far|noticeably|somewhat|a bit|marginally)?\s*" + before, after = sentence[:kw.start()], sentence[kw.end():] + for side, pat in (("a", pa), ("b", pb)): + if re.search(pat + glue + r"$", before): + return side + lead = r"^\s*(?:one|city|location|option|result)?\s*(?:is|was|:|=|-|–|—|would be)?\s*" + for side, pat in (("a", pa), ("b", pb)): + if re.match(lead + pat, after): + return side + last_a = max((m.end() for m in re.finditer(pa, before)), default=-1) + last_b = max((m.end() for m in re.finditer(pb, before)), default=-1) + if last_a >= 0 or last_b >= 0: + return "a" if last_a > last_b else "b" + first_a = next((m.start() for m in re.finditer(pa, after)), None) + first_b = next((m.start() for m in re.finditer(pb, after)), None) + if first_a is None and first_b is None: + return None + if first_b is None or (first_a is not None and first_a < first_b): + return "a" + return "b" + + +def names_winner(text, winner: Sequence[str], loser: Sequence[str], + keywords: Sequence[str], inverse: Sequence[str] = ()) -> bool: + """The answer must attribute an affirmative `keywords` word (``cooler``) to + the winner, or an `inverse` word (``warmer``) to the loser, in at least one + sentence, and never the other way round.""" + t = normalize_text(text) + support = contradict = 0 + for sentence in re.split(r"[.;!?\n]+", t): + for kws, expect in ((keywords, "a"), (inverse, "b")): + for kw in kws: + for m in re.finditer(rf"\b{re.escape(normalize_text(kw))}\b", sentence): + if not _match_is_affirmative(sentence, m): + continue + side = _subject_near(sentence, m, winner, loser) + if side is None: + continue + if side == expect: + support += 1 + else: + contradict += 1 + return support > 0 and contradict == 0 + + +def extract_years(text): # merriam_webster parity + return re.findall(r"\b(1[5-9]\d{2}|20\d{2})\b", text or "") + + +# ---------------------------------------------------------------- SQLite state +EXPECTED_COLUMNS = { + "user": ["id", "email", "name", "password_hash", "unit"], + "location": ["id", "slug", "city", "region", "country", "postal", "temp", "realfeel", "condition", + "icon", "humidity", "wind", "visibility", "pressure", "uv", "air_quality"], + "forecast": ["id", "location_id", "day_index", "label", "high", "low", "condition", "icon", "precip"], + "hourly": ["id", "location_id", "hour_index", "label", "temp", "condition", "icon", "precip"], + "saved_location": ["id", "user_id", "location_id"], + "alert": ["id", "user_id", "location_id", "alert_type", "enabled"], +} +IMMUTABLE_TABLES = ("location", "forecast", "hourly") +READ_ONLY_TABLES = ("user", "saved_location", "alert") +EXPECTED_INITIAL_COUNTS = {"location": 20, "forecast": 140, "hourly": 240, "user": 4, "saved_location": 2, "alert": 0} +EXPECTED_INITIAL_SAVED = {("alice.j@test.com", "new-york-ny"), ("alice.j@test.com", "boston-ma")} +# sha256 over the location/forecast/hourly rows of the build-generated seed +# (sites/accuweather/app.py seeds them from constants; see verify/README.md). +CATALOG_FINGERPRINT = "05807dac4bd73c96fa663d5950d2fe98142590875f841f140ae1dad116abff3c" + + +def db_query(db_path, sql: str, params: Sequence[Any] = ()) -> list[sqlite3.Row]: + con = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) + con.row_factory = sqlite3.Row + try: + return con.execute(sql, params).fetchall() + finally: + con.close() + + +def table_rows(db_path, table: str) -> list[tuple[Any, ...]]: + if table not in EXPECTED_COLUMNS: + raise ValueError(f"unsupported table: {table}") + return [tuple(r) for r in db_query(db_path, f"SELECT * FROM {table} ORDER BY id")] + + +def table_delta(initial_db, after_db, table: str) -> dict[str, list[Any]]: + before = {int(r[0]): r for r in table_rows(initial_db, table)} + after = {int(r[0]): r for r in table_rows(after_db, table)} + common = before.keys() & after.keys() + return {"added": [after[k] for k in sorted(after.keys() - before.keys())], + "removed": [before[k] for k in sorted(before.keys() - after.keys())], + "changed": [(before[k], after[k]) for k in sorted(common) if before[k] != after[k]]} + + +def tables_unchanged(initial_db, after_db, tables: Iterable[str]) -> dict[str, bool]: + return {t: table_rows(initial_db, t) == table_rows(after_db, t) for t in tables} + + +def rows_unchanged_except(initial_db, after_db, table: str, excluded_ids: Iterable[int]) -> bool: + ex = {int(v) for v in excluded_ids} + before = [r for r in table_rows(initial_db, table) if int(r[0]) not in ex] + after = [r for r in table_rows(after_db, table) if int(r[0]) not in ex] + return before == after + + +def catalog_fingerprint(db_path) -> str: + payload = {t: [list(r) for r in table_rows(db_path, t)] for t in IMMUTABLE_TABLES} + return hashlib.sha256(json.dumps(payload, separators=(",", ":"), sort_keys=True).encode()).hexdigest() + + +def user_by_email(db_path, email: str) -> dict[str, Any] | None: + rows = db_query(db_path, "SELECT id, email, name, password_hash, unit FROM user WHERE lower(email)=lower(?) ORDER BY id LIMIT 1", (email,)) + return dict(rows[0]) if rows else None + + +def user_ids(db_path) -> set[int]: + return {int(r["id"]) for r in db_query(db_path, "SELECT id FROM user")} + + +def new_users(initial_db, after_db) -> list[dict[str, Any]]: + fresh = user_ids(after_db) - user_ids(initial_db) + return [dict(r) for r in db_query(after_db, "SELECT id, email, name, password_hash, unit FROM user ORDER BY id") if int(r["id"]) in fresh] + + +def saved_slugs(db_path, email: str) -> set[str] | None: + u = user_by_email(db_path, email) + if not u: + return None + rows = db_query(db_path, "SELECT l.slug FROM saved_location s JOIN location l ON l.id=s.location_id WHERE s.user_id=?", (u["id"],)) + return {r["slug"] for r in rows} + + +def saved_pairs(db_path) -> set[tuple[str, str]]: + rows = db_query(db_path, "SELECT u.email, l.slug FROM saved_location s JOIN user u ON u.id=s.user_id JOIN location l ON l.id=s.location_id") + return {(normalize_text(r["email"]), r["slug"]) for r in rows} + + +def alert_rows(db_path) -> set[tuple[str, str, str, int]]: + rows = db_query(db_path, "SELECT u.email, l.slug, a.alert_type, a.enabled FROM alert a JOIN user u ON u.id=a.user_id JOIN location l ON l.id=a.location_id") + return {(normalize_text(r["email"]), r["slug"], r["alert_type"], int(r["enabled"])) for r in rows} + + +def saved_words_for(db_path, email="alice.j@test.com"): # merriam_webster parity name + return sorted(saved_slugs(db_path, email) or []) + + +def user_exists(db_path, name=None, email=None): + rows = db_query(db_path, "SELECT name, email FROM user") + return any((name is None or r["name"] == name) and (email is None or normalize_text(r["email"]) == normalize_text(email)) for r in rows) + + +def password_matches(password_hash: str, password: str) -> bool: + """Verify a werkzeug ``scrypt:`` / ``pbkdf2:`` hash with hashlib only.""" + try: + method, salt, digest = str(password_hash).split("$", 2) + except ValueError: + return False + if method.startswith("scrypt:"): + _, n, r, p = method.split(":") + derived = hashlib.scrypt(password.encode(), salt=salt.encode(), n=int(n), r=int(r), p=int(p), + maxmem=132 * 1024 * 1024, dklen=64).hex() + elif method.startswith("pbkdf2:"): + parts = method.split(":") + algo = parts[1] + iterations = int(parts[2]) if len(parts) > 2 else 600000 + derived = hashlib.pbkdf2_hmac(algo, password.encode(), salt.encode(), iterations).hex() + else: + return False + return hmac.compare_digest(derived, digest) + + +def fetch_db(container: str, kind: str) -> str: + if kind not in {"instance", "instance_seed"}: + raise ValueError(f"unsupported DB kind: {kind}") + fd, dest = tempfile.mkstemp(prefix=f"{SITE}_{kind}_", suffix=".db") + os.close(fd) + src = f"{container}:/opt/WebSyn/{SITE}/{kind}/{SITE}.db" + r = subprocess.run(["docker", "cp", src, dest], capture_output=True, text=True) + if r.returncode: + Path(dest).unlink(missing_ok=True) + raise RuntimeError(f"docker cp {src} failed: {r.stderr.strip() or r.stdout.strip()}") + atexit.register(Path(dest).unlink, missing_ok=True) + return dest + + +def resolve_db(arg, container, kind): + if arg: + return arg if Path(arg).is_file() else None + try: + return fetch_db(container, kind) + except (OSError, RuntimeError): + return None + + +def _validate_snapshot_contract(initial_db: str, after_db: str) -> None: + for label, db in (("initial", initial_db), ("after", after_db)): + tables = {r["name"] for r in db_query(db, "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")} + if tables != set(EXPECTED_COLUMNS): + raise ValueError(f"{label} database has unexpected tables: {sorted(tables)}") + for table, columns in EXPECTED_COLUMNS.items(): + observed = [r["name"] for r in db_query(db, f"PRAGMA table_info({table})")] + if observed != columns: + raise ValueError(f"{label}.{table} columns differ: {observed}") + counts = {t: len(table_rows(initial_db, t)) for t in EXPECTED_INITIAL_COUNTS} + if counts != EXPECTED_INITIAL_COUNTS: + raise ValueError(f"initial row counts differ: expected={EXPECTED_INITIAL_COUNTS}, observed={counts}") + emails = {normalize_text(r["email"]) for r in db_query(initial_db, "SELECT email FROM user")} + if emails != set(SEED_USERS): + raise ValueError(f"initial users differ: {sorted(emails)}") + if saved_pairs(initial_db) != EXPECTED_INITIAL_SAVED: + raise ValueError(f"initial saved locations differ: {sorted(saved_pairs(initial_db))}") + fp = catalog_fingerprint(initial_db) + if fp != CATALOG_FINGERPRINT: + raise ValueError(f"initial catalog fingerprint {fp} does not match the frozen seed") + changed = [t for t in IMMUTABLE_TABLES if table_rows(initial_db, t) != table_rows(after_db, t)] + if changed: + raise ValueError(f"immutable catalog tables changed: {changed}") + + +def resolve_snapshots(args: VerifyArgs, task_id: str) -> tuple[str, str]: + """Validated (initial_db, after_db) or fail closed (infra_error).""" + initial_db = resolve_db(args.initial_db, args.container, "instance_seed") + after_db = resolve_db(args.after_db, args.container, "instance") + if not initial_db or not after_db: + fail_closed(task_id, "database_unavailable", + "both initial (instance_seed) and after (instance) accuweather snapshots are required: " + "pass --initial_db/--after_db, place initial.db/after.db in the run dir, or set WH_CONTAINER") + try: + _validate_snapshot_contract(str(initial_db), str(after_db)) + except (OSError, sqlite3.Error, ValueError) as exc: + fail_closed(task_id, "snapshot_contract_invalid", str(exc)) + return str(initial_db), str(after_db) + + +# ---------------------------------------------------------------- judge harness +class Judge: + def __init__(self, task_id: str, no_llm: bool = False): + global _NO_LLM + _NO_LLM = bool(no_llm) + self.task_id = task_id + self.no_llm = no_llm + self.ok = True + self.reason = "" + self.evidence: list[str] = [] + + def check(self, name: str, cond: bool, evidence: str = "", llm: bool = False) -> bool: + if llm: # advisory only: never changes the verdict + self.evidence.append(f"[INFO] {name}: {'skipped (--no_llm)' if self.no_llm else evidence}") + return True + if cond: + self.evidence.append(f"[PASS] {name}: {evidence}") + else: + self.ok = False + if not self.reason: + self.reason = name + self.evidence.append(f"[FAIL] {name}: {evidence}") + return bool(cond) + + def emit(self) -> None: + print(json.dumps({"task_id": self.task_id, "pass": self.ok, + "reason": self.reason or "all checks passed", + "evidence": self.evidence}, ensure_ascii=False, indent=2)) + sys.exit(0 if self.ok else 1) + + +def fail_closed(task_id: str, reason: str, detail: str) -> None: + print(json.dumps({"task_id": task_id, "pass": False, "infra_error": True, "reason": reason, + "evidence": [f"[FAIL] {reason}: {detail}"]}, ensure_ascii=False, indent=2)) + sys.exit(1) + + +def _same_local_origin(url: str, start_url: str) -> bool: + try: + o, s = urlparse(str(url or "")), urlparse(str(start_url or "")) + return (o.scheme == s.scheme == "http" and o.hostname is not None and s.hostname is not None + and not o.username and not o.password and o.port == s.port + and o.hostname.casefold() == s.hostname.casefold() and is_site_url(url)) + except ValueError: + return False + + +def check_trajectory_identity(judge: Judge, traj, task_id: str) -> None: + judge.check("final_answer_nonempty", bool(final_answer(traj)), f"final_answer={final_answer(traj)!r}") + judge.check("trajectory_task_matches", str(traj.get("task_id") or "").strip() == task_id, + f"expected={task_id!r}, observed={traj.get('task_id')!r}") + judge.check("trajectory_completed", + traj.get("terminated") is True and traj.get("termination_reason") == "agent_done", + f"terminated={traj.get('terminated')!r}, reason={traj.get('termination_reason')!r}") + steps = traj.get("steps") + judge.check("trajectory_has_steps", isinstance(steps, list) and bool(steps), + f"steps={len(steps) if isinstance(steps, list) else 'invalid'}") + recorded = trajectory_urls(traj) + judge.check("all_urls_match_local_origin", + bool(recorded) and all(_same_local_origin(u, traj.get("start_url", "")) for u in recorded), + f"start_url={traj.get('start_url')!r}, recorded={recorded!r}") + ok, ev = screenshots_decode(traj) + judge.check("screenshots_decode", ok, ev) + + +def check_visited_path(judge: Judge, traj, name: str, path: str) -> bool: + return judge.check(name, navigated_to_path(traj, path), f"required_path={path}") + + +def check_visited_paths(judge: Judge, traj, paths: Iterable[str]) -> None: + for p in paths: + check_visited_path(judge, traj, "visited_" + normalized_url_path(p).strip("/").replace("/", "_"), p) + + +def check_search_surfaces(judge: Judge, traj, slug: str) -> bool: + return judge.check(f"searched_for_{slug}", search_surfaces(traj, slug), + f"a /search?q= visit must surface {slug}; observed_queries={search_queries(traj)!r}") + + +def check_paths_in_order(judge: Judge, traj, name: str, paths: Sequence[str]) -> bool: + urls = [normalized_url_path(u) for u in site_urls(traj)] + cursor = 0 + for p in paths: + expected = normalized_url_path(p) + for i in range(cursor, len(urls)): + if urls[i] == expected: + cursor = i + 1 + break + else: + return judge.check(name, False, f"required_order={list(paths)!r}, observed={urls!r}") + return judge.check(name, True, f"required_order={list(paths)!r}") + + +def check_signed_in_as(judge: Judge, traj, email: str) -> None: + judge.check("visited_login_page", navigated_to_path(traj, "/login"), "required_path=/login") + judge.check("entered_expected_account_email", normalize_text(email) in typed_emails(traj), + f"expected_email={email!r}, typed_emails={typed_emails(traj)!r}") + + +def check_tables_unchanged(judge: Judge, initial_db, after_db, tables: Iterable[str], prefix: str = "") -> None: + for t, same in tables_unchanged(initial_db, after_db, tables).items(): + judge.check(f"{prefix}{t}_unchanged", same, + f"table={t}, initial_rows={len(table_rows(initial_db, t))}, after_rows={len(table_rows(after_db, t))}") + + +def check_read_only(judge: Judge, initial_db, after_db) -> None: + check_tables_unchanged(judge, initial_db, after_db, READ_ONLY_TABLES, prefix="read_only_") + + +def check_saved_delta(judge: Judge, initial_db, after_db, email: str, added: Iterable[str], removed: Iterable[str]) -> None: + """Exact saved_location delta for `email`; every other user's rows and the + user / alert tables must be untouched.""" + before, after = saved_pairs(initial_db), saved_pairs(after_db) + e = normalize_text(email) + exp_added = {(e, s) for s in added} + exp_removed = {(e, s) for s in removed} + judge.check("saved_rows_added", (after - before) == exp_added, f"expected_added={sorted(exp_added)}, observed_added={sorted(after - before)}") + judge.check("saved_rows_removed", (before - after) == exp_removed, f"expected_removed={sorted(exp_removed)}, observed_removed={sorted(before - after)}") + judge.check("no_other_users_saved_changed", + {p for p in before if p[0] != e} == {p for p in after if p[0] != e}, "other users' saved rows identical") + check_tables_unchanged(judge, initial_db, after_db, ("user", "alert")) + + +# ---------------------------------------------------------------- LLM utilities (parity only) +_NO_LLM = False + + +def _llm_config(): + return (os.environ.get("OPENAI_API_KEY", ""), os.environ.get("OPENAI_BASE_URL", ""), os.environ.get("JUDGE_MODEL", "")) + + +def _chat(messages, max_tokens=1024): + if _NO_LLM: + return None + key, base, model = _llm_config() + if not (key and base and model): + return None + payload = {"model": model, "messages": messages, "max_tokens": max_tokens, "temperature": 1.0} + req = urllib.request.Request(base.rstrip("/") + "/chat/completions", data=json.dumps(payload).encode(), + headers={"Content-Type": "application/json", "Authorization": f"Bearer {key}"}) + try: + data = json.loads(urllib.request.urlopen(req, timeout=180).read()) + return data["choices"][0]["message"]["content"] + except Exception: # noqa: BLE001 + return None + + +def _verdict(out): + if not out: + return False, "" + s = out.strip() + return s.upper().startswith("PASS"), s + + +def llm_text_match(agent_answer, ground_truth, question): + """Anchored consistency check; advisory only (see Judge.check(llm=True)).""" + if _NO_LLM: + return False, "[skipped: --no_llm]" + return _verdict(_chat([{"role": "user", "content": + f"You are a STRICT binary grader.\nQuestion: {question}\n" + f"Ground-truth answer (ANCHOR — judge against THIS, never use your own knowledge): {ground_truth}\n" + f"Agent's answer: {agent_answer}\nLine 1: PASS or FAIL. Line 2: one-sentence reason."}])) + + +def llm_screenshot_shows(shot_path, must_show, question=""): + if _NO_LLM: + return False, "[skipped: --no_llm]" + b64 = base64.b64encode(Path(shot_path).read_bytes()).decode() + return _verdict(_chat([{"role": "user", "content": [ + {"type": "text", "text": f"Only what is VISIBLY rendered counts. Question: {question}\nExpected content: {must_show}\nLine 1: PASS or FAIL. Line 2: quote the visible evidence."}, + {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}}]}])) From dcbf85e4e22d45130f81997edc56da1b32b020f7 Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:25:06 -0700 Subject: [PATCH 4/9] fix(accuweather): harden redirects, sessions, form input and error handling Second-pass audit findings against the maintainer merge checklist. Each item was reproduced with a request and re-verified after the fix. - Open redirect: POST /login?next=https://evil.example returned 302 to that URL; /save/ and /units/ did the same via request.referrer. Added local_path()/local_referrer() so only same-origin paths are honoured. - Fixed SECRET_KEY "accuweather-local-benchmark" let a forged session cookie read /account as any user. Now os.environ ACCUWEATHER_SECRET_KEY or secrets.token_hex(32), matching walmart_careers / rotten_tomatoes. - A tampered cookie carrying a non-scalar user_id raised on db.session.get and returned 500; current_user() now ignores anything that is not an int. - /register persisted an empty email, "notanemail" and a 200 000-character name, all returning 302 to /account. SQLite does not enforce VARCHAR length, so the app now bounds email/name/password and returns 400. - /alerts/ wrote any alert_type the client sent, including a 3 000-char value; now whitelisted to severe/rain/temperature, else 400. - /settings silently ignored unit=K; now 400. - No MAX_CONTENT_LENGTH: a 60 MB multipart POST was accepted. Now 64 KB. - No CSRF protection, unlike 23 of the 25 sites. Added CSRFProtect and a token to every POST form (login, register, alerts, settings, save, logout). - No errorhandler and no error template: 404 rendered the bare Werkzeug page. Added templates/error.html and handlers for 400/403/404/405/413/500. - Removed the orphan POST /units/ route (no template ever rendered a control for it). - weather.html/account.html also pick up the unit letter and UV row here because these templates are single-line and their CSRF token cannot be staged separately. Flask-WTF is already installed in the image; added to requirements.txt. Verified: 297-test unit harness OK, 20/20 real-browser task matrix OK. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- sites/accuweather/app.py | 89 ++++++++++++++++++----- sites/accuweather/requirements.txt | 1 + sites/accuweather/templates/account.html | 2 +- sites/accuweather/templates/alerts.html | 2 +- sites/accuweather/templates/error.html | 1 + sites/accuweather/templates/login.html | 2 +- sites/accuweather/templates/register.html | 2 +- sites/accuweather/templates/settings.html | 2 +- sites/accuweather/templates/weather.html | 2 +- 9 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 sites/accuweather/templates/error.html diff --git a/sites/accuweather/app.py b/sites/accuweather/app.py index b92b1a063..323e70ffb 100644 --- a/sites/accuweather/app.py +++ b/sites/accuweather/app.py @@ -1,15 +1,24 @@ """Deterministic, interaction-complete AccuWeather mirror.""" -import os, re +import os, re, secrets from datetime import datetime from functools import wraps -from flask import Flask, flash, redirect, render_template, request, session, url_for +from urllib.parse import urlsplit +from flask import Flask, abort, flash, redirect, render_template, request, session, url_for from flask_sqlalchemy import SQLAlchemy +from flask_wtf.csrf import CSRFProtect +from sqlalchemy import event +from sqlalchemy.engine import Engine from werkzeug.security import check_password_hash, generate_password_hash BASE_DIR=os.path.dirname(os.path.abspath(__file__)) app=Flask(__name__,instance_path=os.path.join(BASE_DIR,"instance")) -app.config.update(SECRET_KEY="accuweather-local-benchmark",SQLALCHEMY_DATABASE_URI="sqlite:///accuweather.db",SQLALCHEMY_TRACK_MODIFICATIONS=False) +app.config.update(SECRET_KEY=os.environ.get("ACCUWEATHER_SECRET_KEY") or secrets.token_hex(32),SQLALCHEMY_DATABASE_URI="sqlite:///accuweather.db",SQLALCHEMY_TRACK_MODIFICATIONS=False,MAX_CONTENT_LENGTH=64*1024,WTF_CSRF_TIME_LIMIT=None) db=SQLAlchemy(app) +csrf=CSRFProtect(app) # registers csrf_token() in Jinja; every POST form renders it + +@event.listens_for(Engine,"connect") +def _sqlite_foreign_keys(dbapi_connection,_record): + cursor=dbapi_connection.cursor(); cursor.execute("PRAGMA foreign_keys=ON"); cursor.close() class User(db.Model): id=db.Column(db.Integer,primary_key=True); email=db.Column(db.String(120),unique=True,nullable=False); name=db.Column(db.String(80),nullable=False); password_hash=db.Column(db.String(255),nullable=False); unit=db.Column(db.String(1),default="F",nullable=False) @@ -24,13 +33,32 @@ class SavedLocation(db.Model): class Alert(db.Model): id=db.Column(db.Integer,primary_key=True); user_id=db.Column(db.Integer,db.ForeignKey("user.id"),nullable=False); location_id=db.Column(db.Integer,db.ForeignKey("location.id"),nullable=False); alert_type=db.Column(db.String(30),nullable=False); enabled=db.Column(db.Boolean,default=True,nullable=False); __table_args__=(db.UniqueConstraint("user_id","location_id","alert_type"),) +EMAIL_RE=re.compile(r"[^@\s]+@[^@\s]+\.[^@\s]+") +ALERT_TYPES=("severe","rain","temperature") +UNITS=("F","C") +MAX_EMAIL,MAX_NAME,MAX_PASSWORD=120,80,128 + +def bounded(value,limit): + """Trimmed text, or "" when it is empty or longer than the column allows. + SQLite does not enforce VARCHAR length, so the app has to.""" + text=str(value or "").strip() + return text if 0",methods=["GET","POST"]) @login_required def alerts(slug): loc=Location.query.filter_by(slug=slug).first_or_404() if request.method=="POST": - selected=set(request.form.getlist("alert_type")); Alert.query.filter_by(user_id=current_user().id,location_id=loc.id).delete() + selected=set(request.form.getlist("alert_type")) + if not selected<=set(ALERT_TYPES): abort(400) + Alert.query.filter_by(user_id=current_user().id,location_id=loc.id).delete() for kind in sorted(selected): db.session.add(Alert(user_id=current_user().id,location_id=loc.id,alert_type=kind,enabled=True)) db.session.commit(); flash("Alert preferences updated.") active={a.alert_type for a in Alert.query.filter_by(user_id=current_user().id,location_id=loc.id).all()}; return render_template("alerts.html",location=loc,active=active) @@ -108,27 +153,25 @@ def account(): def settings(): if request.method=="POST": unit=request.form.get("unit") - if unit in {"F","C"}: current_user().unit=unit; session["unit"]=unit; db.session.commit(); flash("Units updated.") + if unit not in UNITS: abort(400) + current_user().unit=unit; session["unit"]=unit; db.session.commit(); flash("Units updated.") return render_template("settings.html") -@app.route("/units/",methods=["POST"]) -def units(unit): - if unit in {"F","C"}: session["unit"]=unit - return redirect(request.referrer or url_for("index")) @app.route("/login",methods=["GET","POST"]) def login(): if request.method=="POST": user=User.query.filter_by(email=request.form.get("email","").lower().strip()).first() - if user and check_password_hash(user.password_hash,request.form.get("password","")): session["user_id"]=user.id; session["unit"]=user.unit; flash("Welcome back."); return redirect(request.args.get("next") or url_for("account")) + if user and check_password_hash(user.password_hash,request.form.get("password","")): session["user_id"]=user.id; session["unit"]=user.unit; flash("Welcome back."); return redirect(local_path(request.args.get("next")) or url_for("account")) flash("Email or password is incorrect.") return render_template("login.html") @app.route("/register",methods=["GET","POST"]) def register(): if request.method=="POST": - email=request.form.get("email","").lower().strip() + email=bounded(request.form.get("email"),MAX_EMAIL).lower(); name=bounded(request.form.get("name"),MAX_NAME); password=request.form.get("password","") + if not email or not EMAIL_RE.fullmatch(email) or not name or len(password)>MAX_PASSWORD: abort(400) if User.query.filter_by(email=email).first(): flash("An account already exists for that email.") - elif len(request.form.get("password",""))<8: flash("Password must be at least 8 characters.") + elif len(password)<8: flash("Password must be at least 8 characters.") else: - user=User(email=email,name=request.form.get("name","Weather fan").strip(),password_hash=generate_password_hash(request.form["password"])); db.session.add(user); db.session.commit(); session["user_id"]=user.id; return redirect(url_for("account")) + user=User(email=email,name=name,password_hash=generate_password_hash(password)); db.session.add(user); db.session.commit(); session["user_id"]=user.id; return redirect(url_for("account")) return render_template("register.html") @app.route("/logout",methods=["POST"]) def logout(): session.clear(); return redirect(url_for("index")) @@ -138,4 +181,14 @@ def about(): return render_template("static_page.html",title="About AccuWeather" def privacy(): return render_template("static_page.html",title="Privacy Statement",copy="Your privacy choices and account preferences are available here.") @app.route("/_health") def health(): return {"ok":True,"site":"accuweather"} +ERROR_COPY={400:"That request was not valid.",403:"That request could not be verified. Please reload the page and try again.",404:"We could not find that page.",405:"That address does not accept this kind of request.",413:"That upload is too large.",500:"Something went wrong on our end."} +@app.errorhandler(400) +@app.errorhandler(403) +@app.errorhandler(404) +@app.errorhandler(405) +@app.errorhandler(413) +@app.errorhandler(500) +def handle_error(error): + code=getattr(error,"code",500); db.session.rollback() + return render_template("error.html",code=code,message=ERROR_COPY.get(code,"Something went wrong.")),code if __name__=="__main__": app.run(host="0.0.0.0",port=int(os.environ.get("PORT",5000)),debug=False) diff --git a/sites/accuweather/requirements.txt b/sites/accuweather/requirements.txt index 0e8f2ee89..dbc8aa63a 100644 --- a/sites/accuweather/requirements.txt +++ b/sites/accuweather/requirements.txt @@ -1,5 +1,6 @@ Flask==3.1.0 Flask-SQLAlchemy==3.1.1 +Flask-WTF==1.2.2 Werkzeug==3.1.3 SQLAlchemy==2.0.36 Jinja2==3.1.4 diff --git a/sites/accuweather/templates/account.html b/sites/accuweather/templates/account.html index ceec1ad5a..583caf2ed 100644 --- a/sites/accuweather/templates/account.html +++ b/sites/accuweather/templates/account.html @@ -1 +1 @@ -{% extends 'base.html' %}{% block content %}

Saved Locations

{% for l in saved %}{{l.city}}, {{l.region}}{{l.condition}}{{display_temp(l.temp)}}°{% else %}
You have no saved locations.
{% endfor %}
{% endblock %} +{% extends 'base.html' %}{% block content %}

Saved Locations

{% for l in saved %}{{l.city}}, {{l.region}}{{l.condition}}{{display_temp(l.temp)}}°{{unit}}{% else %}
You have no saved locations.
{% endfor %}
{% endblock %} diff --git a/sites/accuweather/templates/alerts.html b/sites/accuweather/templates/alerts.html index a00717b8f..a67892932 100644 --- a/sites/accuweather/templates/alerts.html +++ b/sites/accuweather/templates/alerts.html @@ -1 +1 @@ -{% extends 'base.html' %}{% block content %}

{{location.city}} Alerts

Choose the notifications you want to receive.

{% endblock %} +{% extends 'base.html' %}{% block content %}

{{location.city}} Alerts

Choose the notifications you want to receive.

{% endblock %} diff --git a/sites/accuweather/templates/error.html b/sites/accuweather/templates/error.html new file mode 100644 index 000000000..fa2d75343 --- /dev/null +++ b/sites/accuweather/templates/error.html @@ -0,0 +1 @@ +{% extends 'base.html' %}{% block title %}{{code}} | AccuWeather{% endblock %}{% block content %}

{{code}}

{{message}}

Back to the forecast

{% endblock %} diff --git a/sites/accuweather/templates/login.html b/sites/accuweather/templates/login.html index d1c3540f1..c9eb94199 100644 --- a/sites/accuweather/templates/login.html +++ b/sites/accuweather/templates/login.html @@ -1 +1 @@ -{% extends 'base.html' %}{% block content %}

Sign in

New here? Create account

{% endblock %} +{% extends 'base.html' %}{% block content %}

Sign in

New here? Create account

{% endblock %} diff --git a/sites/accuweather/templates/register.html b/sites/accuweather/templates/register.html index 0413e4b25..ea36ea530 100644 --- a/sites/accuweather/templates/register.html +++ b/sites/accuweather/templates/register.html @@ -1 +1 @@ -{% extends 'base.html' %}{% block content %}

Create account

{% endblock %} +{% extends 'base.html' %}{% block content %}

Create account

{% endblock %} diff --git a/sites/accuweather/templates/settings.html b/sites/accuweather/templates/settings.html index c938a82b6..b38081d6a 100644 --- a/sites/accuweather/templates/settings.html +++ b/sites/accuweather/templates/settings.html @@ -1 +1 @@ -{% extends 'base.html' %}{% block content %}

Settings

Temperature unit
{% endblock %} +{% extends 'base.html' %}{% block content %}

Settings

Temperature unit
{% endblock %} diff --git a/sites/accuweather/templates/weather.html b/sites/accuweather/templates/weather.html index d15de9ee5..5a68f00a7 100644 --- a/sites/accuweather/templates/weather.html +++ b/sites/accuweather/templates/weather.html @@ -1 +1 @@ -{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block title %}{{location.city}} Weather | AccuWeather{% endblock %}{% block content %}

{{location.city}}, {{location.region}}

{{location.country}} Weather

{{tabs(location)}}

CURRENT WEATHER

12:24 PM
{{icon(location.icon,location.condition)}}{{display_temp(location.temp)}}°

{{location.condition}}

RealFeel® {{display_temp(location.realfeel)}}°

Wind
{{location.wind}} mph
Humidity
{{location.humidity}}%
Visibility
{{location.visibility}} mi
Pressure
{{'%.2f'|format(location.pressure)}} in
Manage alerts
{% endblock %} +{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block title %}{{location.city}} Weather | AccuWeather{% endblock %}{% block content %}

{{location.city}}, {{location.region}}

{{location.country}} Weather

{{tabs(location)}}

CURRENT WEATHER

12:24 PM
{{icon(location.icon,location.condition)}}{{display_temp(location.temp)}}°{{unit}}

{{location.condition}}

RealFeel® {{display_temp(location.realfeel)}}°{{unit}}

Wind
{{location.wind}} mph
Humidity
{{location.humidity}}%
Visibility
{{location.visibility}} mi
Pressure
{{'%.2f'|format(location.pressure)}} in
UV Index
{{location.uv}}
Manage alerts
{% endblock %} From e901cba71218892a94dc0f94117f8a775714ba65 Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:25:06 -0700 Subject: [PATCH 5/9] fix(accuweather): responsive layout, tab state, units and WCAG contrast - The homepage overflowed 192 px horizontally at 768 px because .layout keeps fixed 632px/304px columns until the 700 px breakpoint. Added a 960 px breakpoint that collapses the grid; 60/60 route x width cells are now clean. - .tabs carried margin-bottom:315px (an ad placeholder), leaving an empty band between the tabs and the card on every location page. Now 24 px / 18 px. - The active tab was hardcoded to .tabs a:first-child, so /hourly highlighted "Current Weather". The macro now marks the tab by request.endpoint and sets aria-current. - Temperatures rendered no unit letter, so Celsius mode showed a bare "26deg". Added the letter on the current, hourly, daily and account surfaces. - Contrast, measured by sampling rendered pixels rather than reading CSS: white on the brand orange #f05514 is 3.49:1 and #f15a18 is 3.38:1, both under the 4.5:1 body-text bar, and .news-grid small was 4.14:1. Introduced an --action token (#c2410c, 5.18:1) for buttons, .premium and the breaking strip, keeping the brand orange for decoration only; the orange "Accu" mark on the dark header measures 4.77:1 and is unchanged. Added a :focus-visible ring (--focus #1a73e8) that clears 3:1 on both the white page and the #222 header. - Made the seven dead "Weather News" / "Top Stories" items real links. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- sites/accuweather/static/css/site.css | 8 +++++--- sites/accuweather/templates/daily.html | 2 +- sites/accuweather/templates/hourly.html | 2 +- sites/accuweather/templates/macros.html | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/sites/accuweather/static/css/site.css b/sites/accuweather/static/css/site.css index be5d4a8f7..b8f5a02ec 100644 --- a/sites/accuweather/static/css/site.css +++ b/sites/accuweather/static/css/site.css @@ -1,7 +1,9 @@ -@font-face{font-family:Solis;src:url('../fonts/Solis-Regular.woff2')}*{box-sizing:border-box}html,body{margin:0;background:#f1f1f1;color:#292929;font-family:Solis,Arial,sans-serif}a{color:inherit;text-decoration:none}button,.button{border:0;border-radius:2px;background:#f05514;color:#fff;padding:12px 22px;font:inherit;cursor:pointer}input{font:inherit}.corp{height:34px;background:#1d1d1d;color:#bbb;display:flex;justify-content:flex-end;gap:28px;padding:8px calc((100% - 1180px)/2);font-size:12px;white-space:nowrap}.breaking{background:#303030;color:#fff;text-align:center;padding:8px;font-size:13px}header{height:66px;background:#222;color:#fff;display:flex;align-items:center;gap:24px;padding:0 max(24px,calc((100% - 1180px)/2));position:relative;z-index:2}.logo{font-size:28px;white-space:nowrap}.logo b{color:#f25b24}.menu{font-size:22px}.search{display:flex;max-width:470px;flex:1;margin:auto}.search input{min-width:0;flex:1;padding:12px;border:0}.search button{font-size:24px;padding:4px 15px}.premium{color:#ffb294}.flash{background:#fff4d5;border-left:5px solid #f05514;max-width:920px;margin:18px auto 0;padding:14px}main{min-height:70vh}.hero{height:170px;background:linear-gradient(135deg,#3d6685,#acc7d8);display:grid;place-items:center}.hero form{display:flex;width:min(700px,90%)}.hero input{flex:1;min-width:0;padding:18px;border:0;font-size:17px}.layout{max-width:1180px;margin:24px auto;display:grid;grid-template-columns:minmax(0,2fr) minmax(260px,1fr);gap:22px}.narrow{max-width:920px;margin:32px auto;padding:0 18px}.card{background:white;box-shadow:0 2px 5px #0002;padding:22px}.eyebrow{font-size:13px;letter-spacing:.08em;color:#666}.current{display:grid;grid-template-columns:1fr 100px 100px 1fr;align-items:center;gap:20px;border-top:3px solid #f05514}.current h1{font-size:23px;margin:0}.current strong,.reading strong{font-size:62px;font-weight:400}.weather-icon{width:72px;height:72px;object-fit:contain}.city-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}.city{display:grid;grid-template-columns:1fr 55px 60px;align-items:center;gap:8px}.city .weather-icon{width:50px;height:50px}.city span{grid-column:1/-1;color:#666}.tabs{display:flex;overflow-x:auto;background:#fff;border-bottom:1px solid #ddd;margin:20px 0}.tabs a{padding:15px 18px;white-space:nowrap}.tabs a:hover{border-bottom:3px solid #f05514}.weather-main{display:grid;grid-template-columns:1fr 1fr;gap:18px;border-top:3px solid #f05514}.reading{display:flex;align-items:center;justify-content:flex-end}.weather-main dl{grid-column:1/-1;margin:0}.weather-main dl div{display:flex;justify-content:space-between;border-top:1px solid #ddd;padding:10px}.weather-main dd{margin:0;font-weight:bold}.actions,.account-head{display:flex;align-items:center;gap:15px;margin:18px 0}.account-head h1{margin-right:auto}.results{background:#fff;box-shadow:0 2px 5px #0002}.result{display:flex;align-items:center;gap:15px;padding:17px;border-bottom:1px solid #ddd}.result img{width:26px;height:26px}.result span{display:flex;flex-direction:column;flex:1}.result small{color:#666;margin-top:4px}.result i{font-size:28px}.list{padding:0}.list>div{display:grid;grid-template-columns:90px 70px 100px 1fr 150px;align-items:center;gap:12px;padding:15px 20px;border-bottom:1px solid #ddd}.list .weather-icon{width:52px;height:52px}.radar{height:440px;position:relative;overflow:hidden;background:repeating-linear-gradient(0deg,transparent 0 39px,#ffffff30 40px),repeating-linear-gradient(90deg,transparent 0 39px,#ffffff30 40px),linear-gradient(145deg,#406942,#a5bf83 40%,#496b48);color:#fff}.radar:after{content:"";position:absolute;inset:25px;border:1px solid #ffffff66;border-radius:50%}.radar span{position:absolute;left:47%;top:51%;z-index:3;background:#222c;padding:4px}.storm{position:absolute;border-radius:50%;filter:blur(8px);z-index:2}.storm.one{width:200px;height:100px;background:#51d969;left:13%;top:16%;box-shadow:45px 15px 15px #ffdb3b}.storm.two{width:170px;height:80px;background:#33b759;right:10%;bottom:15%;box-shadow:-30px -10px 20px #ff9d22}.legend{text-align:right;padding:10px}.legend i{display:inline-block;width:18px;height:10px;background:#51d969;margin-left:20px}.legend i+*{}.aq{display:flex;align-items:center;gap:25px;border-left:7px solid #65a747}.aq-number{font-size:55px;color:#4d8d31}.meter{height:14px;background:#ddd}.meter i{display:block;height:100%;background:#65a747}.auth{max-width:480px;margin:45px auto}.auth form{display:flex;flex-direction:column;gap:18px}.auth label{display:flex;flex-direction:column;gap:7px}.auth input:not([type=radio]):not([type=checkbox]){padding:12px;border:1px solid #aaa}.auth fieldset{display:flex;flex-direction:column;gap:15px;border:1px solid #ddd}.empty{text-align:center}.prose{margin-top:40px}footer{margin-top:60px;background:#252525;color:#bbb;padding:35px max(24px,calc((100% - 1180px)/2))}footer div{display:flex;gap:25px;color:#fff}.sr{position:absolute;left:-9999px} +:root{--brand:#f05514;--action:#c2410c;--focus:#1a73e8} +@font-face{font-family:Solis;src:url('../fonts/Solis-Regular.woff2')}*{box-sizing:border-box}html,body{margin:0;background:#f1f1f1;color:#292929;font-family:Solis,Arial,sans-serif}a{color:inherit;text-decoration:none}button,.button{border:0;border-radius:2px;background:var(--action);color:#fff;padding:12px 22px;font:inherit;cursor:pointer}input{font:inherit}:focus-visible{outline:3px solid var(--focus);outline-offset:2px}.corp{height:34px;background:#1d1d1d;color:#bbb;display:flex;justify-content:flex-end;gap:28px;padding:8px calc((100% - 1180px)/2);font-size:12px;white-space:nowrap}.breaking{background:#303030;color:#fff;text-align:center;padding:8px;font-size:13px}header{height:66px;background:#222;color:#fff;display:flex;align-items:center;gap:24px;padding:0 max(24px,calc((100% - 1180px)/2));position:relative;z-index:2}.logo{font-size:28px;white-space:nowrap}.logo b{color:#f25b24}.menu{font-size:22px}.search{display:flex;max-width:470px;flex:1;margin:auto}.search input{min-width:0;flex:1;padding:12px;border:0}.search button{font-size:24px;padding:4px 15px}.premium{color:#ffb294}.flash{background:#fff4d5;border-left:5px solid #f05514;max-width:920px;margin:18px auto 0;padding:14px}main{min-height:70vh}.hero{height:170px;background:linear-gradient(135deg,#3d6685,#acc7d8);display:grid;place-items:center}.hero form{display:flex;width:min(700px,90%)}.hero input{flex:1;min-width:0;padding:18px;border:0;font-size:17px}.layout{max-width:1180px;margin:24px auto;display:grid;grid-template-columns:minmax(0,2fr) minmax(260px,1fr);gap:22px}.narrow{max-width:920px;margin:32px auto;padding:0 18px}.card{background:white;box-shadow:0 2px 5px #0002;padding:22px}.eyebrow{font-size:13px;letter-spacing:.08em;color:#666}.current{display:grid;grid-template-columns:1fr 100px 100px 1fr;align-items:center;gap:20px;border-top:3px solid #f05514}.current h1{font-size:23px;margin:0}.current strong,.reading strong{font-size:62px;font-weight:400}.weather-icon{width:72px;height:72px;object-fit:contain}.city-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}.city{display:grid;grid-template-columns:1fr 55px 60px;align-items:center;gap:8px}.city .weather-icon{width:50px;height:50px}.city span{grid-column:1/-1;color:#666}.tabs{display:flex;overflow-x:auto;background:#fff;border-bottom:1px solid #ddd;margin:20px 0}.tabs a{padding:15px 18px;white-space:nowrap}.tabs a:hover{border-bottom:3px solid #f05514}.weather-main{display:grid;grid-template-columns:1fr 1fr;gap:18px;border-top:3px solid #f05514}.reading{display:flex;align-items:center;justify-content:flex-end}.weather-main dl{grid-column:1/-1;margin:0}.weather-main dl div{display:flex;justify-content:space-between;border-top:1px solid #ddd;padding:10px}.weather-main dd{margin:0;font-weight:bold}.actions,.account-head{display:flex;align-items:center;gap:15px;margin:18px 0}.account-head h1{margin-right:auto}.results{background:#fff;box-shadow:0 2px 5px #0002}.result{display:flex;align-items:center;gap:15px;padding:17px;border-bottom:1px solid #ddd}.result img{width:26px;height:26px}.result span{display:flex;flex-direction:column;flex:1}.result small{color:#666;margin-top:4px}.result i{font-size:28px}.list{padding:0}.list>div{display:grid;grid-template-columns:90px 70px 100px 1fr 150px;align-items:center;gap:12px;padding:15px 20px;border-bottom:1px solid #ddd}.list .weather-icon{width:52px;height:52px}.radar{height:440px;position:relative;overflow:hidden;background:repeating-linear-gradient(0deg,transparent 0 39px,#ffffff30 40px),repeating-linear-gradient(90deg,transparent 0 39px,#ffffff30 40px),linear-gradient(145deg,#406942,#a5bf83 40%,#496b48);color:#fff}.radar:after{content:"";position:absolute;inset:25px;border:1px solid #ffffff66;border-radius:50%}.radar span{position:absolute;left:47%;top:51%;z-index:3;background:#222c;padding:4px}.storm{position:absolute;border-radius:50%;filter:blur(8px);z-index:2}.storm.one{width:200px;height:100px;background:#51d969;left:13%;top:16%;box-shadow:45px 15px 15px #ffdb3b}.storm.two{width:170px;height:80px;background:#33b759;right:10%;bottom:15%;box-shadow:-30px -10px 20px #ff9d22}.legend{text-align:right;padding:10px}.legend i{display:inline-block;width:18px;height:10px;background:#51d969;margin-left:20px}.legend i+*{}.aq{display:flex;align-items:center;gap:25px;border-left:7px solid #65a747}.aq-number{font-size:55px;color:#4d8d31}.meter{height:14px;background:#ddd}.meter i{display:block;height:100%;background:#65a747}.auth{max-width:480px;margin:45px auto}.auth form{display:flex;flex-direction:column;gap:18px}.auth label{display:flex;flex-direction:column;gap:7px}.auth input:not([type=radio]):not([type=checkbox]){padding:12px;border:1px solid #aaa}.auth fieldset{display:flex;flex-direction:column;gap:15px;border:1px solid #ddd}.empty{text-align:center}.prose{margin-top:40px}footer{margin-top:60px;background:#252525;color:#bbb;padding:35px max(24px,calc((100% - 1180px)/2))}footer div{display:flex;gap:25px;color:#fff}.sr{position:absolute;left:-9999px} @media(max-width:700px){.corp{display:none}.breaking{font-size:11px}.premium{display:none}header{padding:0 12px;gap:10px}.logo{font-size:20px}.search input{width:80px}.layout{display:block;margin:16px}.layout aside{margin-top:18px}.current{grid-template-columns:1fr 60px}.current strong{font-size:48px}.current>div:last-child{grid-column:1/-1}.city-grid{grid-template-columns:1fr}.tabs{margin-left:-18px;margin-right:-18px}.weather-main{grid-template-columns:1fr}.reading{justify-content:flex-start}.list>div{grid-template-columns:55px 50px 70px 1fr}.list>div span:last-child{grid-column:1/-1}.radar{height:330px}.account-head{flex-wrap:wrap}.account-head h1{width:100%}footer div{flex-wrap:wrap}} @media(max-width:360px){header{gap:6px}.logo{font-size:18px}.menu{display:none}.search button{padding:4px 9px}.current{padding:15px}.city{grid-template-columns:1fr 45px 45px}.narrow{padding:0 10px}.tabs{margin-left:-10px;margin-right:-10px}.tabs a{padding:13px 12px}.list>div{padding:12px 10px;gap:7px}} /* Captured AccuWeather proportions: a compact 960px weather canvas on a pale-blue page. */ -html,body{background:#e7edf5}.corp{height:32px;padding:7px 20px;justify-content:flex-start;gap:12px}.corp span+span{border-left:1px solid #777;padding-left:12px}.breaking{position:absolute;left:40%;right:0;top:0;height:32px;background:#f15a18;text-align:left;padding:8px 20px;z-index:4}header{height:62px;padding:0 max(20px,calc((100% - 960px)/2));gap:17px}.logo{font-size:25px}.search{height:34px;max-width:325px;margin-left:auto;margin-right:0}.search input{padding:8px 12px;background:#fff}.search button{font-size:18px;padding:2px 12px}.premium{background:white;color:#f05514;padding:6px 10px;border-radius:18px;font-size:11px}.hero{height:auto;background:transparent;display:block;max-width:632px;margin:22px auto 12px}.hero form{width:100%;height:42px;box-shadow:0 1px 4px #0002}.hero input{padding:12px 15px;font-size:14px}.hero button{padding:8px 18px}.layout{max-width:960px;margin:20px auto;grid-template-columns:632px 304px;gap:24px}.layout>section>h2{font-size:19px}.layout aside{box-shadow:none}.card{box-shadow:none}.current{grid-template-columns:1fr 70px 78px 1fr;padding:18px;border-top:0}.current strong,.reading strong{font-size:52px}.city-grid{display:block}.city{min-height:56px;grid-template-columns:1fr 45px 55px 110px;border-bottom:1px solid #d5d8dc;padding:8px 15px}.city span{grid-column:auto;text-align:right}.narrow{max-width:960px;margin:28px auto;padding:0}.narrow>h1,.narrow>p{max-width:632px}.tabs{border-bottom:1px solid #bbc0c7;background:transparent;margin:0 0 315px}.tabs a{padding:13px 20px;color:#656b72;font-size:14px}.tabs a:first-child{border-bottom:2px solid #f05514}.weather-main{width:632px;max-width:100%;border-top:0;padding:12px}.weather-main>div:first-child{grid-column:1/-1;border-bottom:1px solid #ddd;display:flex;justify-content:space-between}.weather-main>div:first-child .eyebrow{margin:0}.weather-main .reading{justify-content:flex-start}.weather-main dl{display:grid;grid-template-columns:1fr 1fr;gap:0 18px}.weather-main dl div{padding:11px 0}.actions{max-width:632px}.results{max-width:632px}.result{background:#fff}.list{max-width:632px}.aq,.radar{max-width:632px}.homepage-title{font-size:13px;letter-spacing:.08em;color:#666}.news-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:#ddd}.news-grid article{background:#f5f6f7;padding:15px;min-height:120px}.news-grid h3{font-size:14px;margin:0 0 28px}.news-grid small{color:#777} -@media(max-width:700px){.breaking{position:static;height:auto}.corp{display:none}header{height:54px}.menu{order:4}.logo{font-size:21px}.search{position:absolute;top:58px;left:6px;right:6px;max-width:none;height:36px}.premium{display:none}.hero{margin:48px 6px 12px}.layout{margin:12px 6px;display:block}.layout aside{margin-top:12px}.current{grid-template-columns:minmax(0,1fr) 42px 52px;gap:6px}.current>div:last-child{grid-column:1/-1}.city{grid-template-columns:minmax(0,1fr) 38px 42px 76px;gap:5px;font-size:12px}.narrow{padding:0 8px;margin-top:48px}.tabs{margin:0 -8px 210px}.tabs a{padding:12px 14px}.weather-main{width:100%}.weather-main dl{grid-template-columns:1fr}.news-grid{grid-template-columns:repeat(2,1fr)}} +html,body{background:#e7edf5}.corp{height:32px;padding:7px 20px;justify-content:flex-start;gap:12px}.corp span+span{border-left:1px solid #777;padding-left:12px}.breaking{position:absolute;left:40%;right:0;top:0;height:32px;background:var(--action);text-align:left;padding:8px 20px;z-index:4}header{height:62px;padding:0 max(20px,calc((100% - 960px)/2));gap:17px}.logo{font-size:25px}.search{height:34px;max-width:325px;margin-left:auto;margin-right:0}.search input{padding:8px 12px;background:#fff}.search button{font-size:18px;padding:2px 12px}.premium{background:white;color:var(--action);padding:6px 10px;border-radius:18px;font-size:11px}.hero{height:auto;background:transparent;display:block;max-width:632px;margin:22px auto 12px}.hero form{width:100%;height:42px;box-shadow:0 1px 4px #0002}.hero input{padding:12px 15px;font-size:14px}.hero button{padding:8px 18px}.layout{max-width:960px;margin:20px auto;grid-template-columns:632px 304px;gap:24px}.layout>section>h2{font-size:19px}.layout aside{box-shadow:none}.card{box-shadow:none}.current{grid-template-columns:1fr 70px 78px 1fr;padding:18px;border-top:0}.current strong,.reading strong{font-size:52px}.city-grid{display:block}.city{min-height:56px;grid-template-columns:1fr 45px 55px 110px;border-bottom:1px solid #d5d8dc;padding:8px 15px}.city span{grid-column:auto;text-align:right}.narrow{max-width:960px;margin:28px auto;padding:0}.narrow>h1,.narrow>p{max-width:632px}.tabs{border-bottom:1px solid #bbc0c7;background:transparent;margin:0 0 24px}.tabs a{padding:13px 20px;color:#656b72;font-size:14px}.tabs a.active{border-bottom:2px solid var(--brand);color:#292929}.weather-main{width:632px;max-width:100%;border-top:0;padding:12px}.weather-main>div:first-child{grid-column:1/-1;border-bottom:1px solid #ddd;display:flex;justify-content:space-between}.weather-main>div:first-child .eyebrow{margin:0}.weather-main .reading{justify-content:flex-start}.weather-main dl{display:grid;grid-template-columns:1fr 1fr;gap:0 18px}.weather-main dl div{padding:11px 0}.actions{max-width:632px}.results{max-width:632px}.result{background:#fff}.list{max-width:632px}.aq,.radar{max-width:632px}.homepage-title{font-size:13px;letter-spacing:.08em;color:#666}.news-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:#ddd}.news-grid article{background:#f5f6f7;padding:15px;min-height:120px}.news-grid h3{font-size:14px;margin:0 0 28px}.news-grid small{color:#666} +@media(max-width:960px){.layout{display:block;margin:20px 16px}.layout aside{margin-top:18px}} +@media(max-width:700px){.breaking{position:static;height:auto}.corp{display:none}header{height:54px}.menu{order:4}.logo{font-size:21px}.search{position:absolute;top:58px;left:6px;right:6px;max-width:none;height:36px}.premium{display:none}.hero{margin:48px 6px 12px}.layout{margin:12px 6px;display:block}.layout aside{margin-top:12px}.current{grid-template-columns:minmax(0,1fr) 42px 52px;gap:6px}.current>div:last-child{grid-column:1/-1}.city{grid-template-columns:minmax(0,1fr) 38px 42px 76px;gap:5px;font-size:12px}.narrow{padding:0 8px;margin-top:48px}.tabs{margin:0 -8px 18px}.tabs a{padding:12px 14px}.weather-main{width:100%}.weather-main dl{grid-template-columns:1fr}.news-grid{grid-template-columns:repeat(2,1fr)}} diff --git a/sites/accuweather/templates/daily.html b/sites/accuweather/templates/daily.html index 488ac56b1..10ce5f5d0 100644 --- a/sites/accuweather/templates/daily.html +++ b/sites/accuweather/templates/daily.html @@ -1 +1 @@ -{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block content %}

{{location.city}} Daily Weather

{{tabs(location)}}
{% for d in location.forecasts|sort(attribute='day_index') %}
{{d.label}}{{icon(d.icon,d.condition)}}{{display_temp(d.high)}}° / {{display_temp(d.low)}}°{{d.condition}}{{d.precip}}% precipitation
{% endfor %}
{% endblock %} +{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block content %}

{{location.city}} Daily Weather

{{tabs(location)}}
{% for d in location.forecasts|sort(attribute='day_index') %}
{{d.label}}{{icon(d.icon,d.condition)}}{{display_temp(d.high)}}° / {{display_temp(d.low)}}°{{unit}}{{d.condition}}{{d.precip}}% precipitation
{% endfor %}
{% endblock %} diff --git a/sites/accuweather/templates/hourly.html b/sites/accuweather/templates/hourly.html index b9ef92451..d8d36ad0d 100644 --- a/sites/accuweather/templates/hourly.html +++ b/sites/accuweather/templates/hourly.html @@ -1 +1 @@ -{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block content %}

{{location.city}} Hourly Weather

{{tabs(location)}}
{% for h in location.hourly|sort(attribute='hour_index') %}
{{h.label}}{{icon(h.icon,h.condition)}}{{display_temp(h.temp)}}°{{h.condition}}{{h.precip}}% precipitation
{% endfor %}
{% endblock %} +{% extends 'base.html' %}{% from 'macros.html' import icon,tabs %}{% block content %}

{{location.city}} Hourly Weather

{{tabs(location)}}
{% for h in location.hourly|sort(attribute='hour_index') %}
{{h.label}}{{icon(h.icon,h.condition)}}{{display_temp(h.temp)}}°{{unit}}{{h.condition}}{{h.precip}}% precipitation
{% endfor %}
{% endblock %} diff --git a/sites/accuweather/templates/macros.html b/sites/accuweather/templates/macros.html index 6b1c9171f..e43c4f1aa 100644 --- a/sites/accuweather/templates/macros.html +++ b/sites/accuweather/templates/macros.html @@ -1,2 +1,2 @@ {% macro icon(code,alt) %}{{alt}}{% endmacro %} -{% macro tabs(loc) %}{% endmacro %} +{% macro tabs(loc) %}{% endmacro %} From 94abf80800c72478e4cf525b625fd1284d107c7f Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:25:06 -0700 Subject: [PATCH 6/9] fix(accuweather): reject stub screenshots and non-discriminative search gates Two gaps in our own grading contract, both found by probing the verifiers rather than reading them. - screenshots_decode only required width and height >= 1, so a run whose every screenshot was a 1x1 PNG passed verify_0 with "all checks passed". The checklist asks for "decodable and above a minimum size"; now 200x150. The test fixture PNG moves from 2x2 to 320x200 accordingly. - check_search_surfaces accepted any query token intersecting the target's city/region/country/postal. Every US location carries country "United States", so a single q=United+States search satisfied searched_for_springfield-mo without the agent disambiguating the three Springfields. search_surfaces now ignores tokens shared by more than half the catalog, derived from CATALOG rather than a hardcoded stop-list, so "united"/"states" no longer count while "springfield" and "65806" do. Added negative tests for both (test_stub_screenshot_fails, test_catalog_wide_query_does_not_satisfy_the_search_gate, test_screenshots_reject_stub_sizes, test_search_gate_ignores_catalog_wide_tokens). Harness 293 -> 297 tests, OK; 20/20 real-browser matrix still OK. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- sites/accuweather/verify/tests/_support.py | 6 ++-- .../accuweather/verify/tests/test_verify_0.py | 4 +++ .../accuweather/verify/tests/test_verify_5.py | 11 +++++++ .../verify/tests/test_verify_lib.py | 30 +++++++++++++++++++ sites/accuweather/verify/verify_lib.py | 23 ++++++++++---- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/sites/accuweather/verify/tests/_support.py b/sites/accuweather/verify/tests/_support.py index a12935650..8a9d19bad 100644 --- a/sites/accuweather/verify/tests/_support.py +++ b/sites/accuweather/verify/tests/_support.py @@ -178,7 +178,7 @@ def write(self, path: Path) -> Path: return path -def make_png(width: int = 2, height: int = 2) -> bytes: +def make_png(width: int = 320, height: int = 200) -> bytes: raw = b"".join(b"\x00" + b"\x10\x20\x30" * width for _ in range(height)) def chunk(kind: bytes, data: bytes) -> bytes: @@ -201,7 +201,7 @@ def login_steps(email: str, password: str = PASSWORD) -> list[dict[str, Any]]: def write_run(run_dir: Path, task_id: str, steps: list[dict[str, Any]], answer: str, *, base: str = BASE, start_url: str | None = None, updates: dict[str, Any] | None = None, - corrupt_screenshot: bool = False) -> Path: + corrupt_screenshot: bool = False, stub_screenshot: bool = False) -> Path: shots = run_dir / "screenshots" shots.mkdir(parents=True, exist_ok=True) logged = [] @@ -220,6 +220,8 @@ def write_run(run_dir: Path, task_id: str, steps: list[dict[str, Any]], answer: (shots / f"step_{i:03d}.png").write_bytes(PNG) if corrupt_screenshot: (shots / "step_001.png").write_bytes(b"definitely not a png") + if stub_screenshot: # decodable, but a 1x1 placeholder rather than a page + (shots / "step_001.png").write_bytes(make_png(1, 1)) traj = {"task_id": task_id, "task": "", "start_url": start_url or (base + "/"), "max_steps": 15, "steps": logged, "terminated": True, "termination_reason": "agent_done", "final_answer": answer, "success_self_report": True, "verifier_path": f"sites/accuweather/verify/verify_{task_id.rsplit('--', 1)[1]}.py", "judge_rubric": ""} diff --git a/sites/accuweather/verify/tests/test_verify_0.py b/sites/accuweather/verify/tests/test_verify_0.py index 5e2d3380a..7e814060d 100644 --- a/sites/accuweather/verify/tests/test_verify_0.py +++ b/sites/accuweather/verify/tests/test_verify_0.py @@ -56,6 +56,10 @@ def test_mixed_origin_run_fails(self) -> None: def test_corrupt_screenshot_fails(self) -> None: self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), corrupt_screenshot=True), "screenshots_decode") + def test_stub_screenshot_fails(self) -> None: + """A 1x1 PNG decodes cleanly but is not evidence that a page was seen.""" + self.assertFailsOn(self.verdict(C["steps"], C["answer"], after=genuine_after(), stub_screenshot=True), "screenshots_decode") + def test_schema_change_fails_closed(self) -> None: after = genuine_after() after.extra_sql.append("CREATE TABLE injected(id INTEGER PRIMARY KEY)") diff --git a/sites/accuweather/verify/tests/test_verify_5.py b/sites/accuweather/verify/tests/test_verify_5.py index 792a7787d..98c412a9f 100644 --- a/sites/accuweather/verify/tests/test_verify_5.py +++ b/sites/accuweather/verify/tests/test_verify_5.py @@ -37,6 +37,17 @@ def test_other_task_trajectory_fails(self) -> None: def test_shortcut_fails_on_navigation_gate(self) -> None: self.assertFailsOn(self.verdict([step("/"), step("/", "done")], C["answer"], after=genuine_after()), C["gate"]) + def test_catalog_wide_query_does_not_satisfy_the_search_gate(self) -> None: + """The point of this gate is that the agent disambiguated three + Springfields. ``q=United States`` matches every US location, so it must + not stand in for a real search; ``q=Springfield`` must.""" + universal = [step("/"), step("/search?q=United%20States"), step("/weather/springfield-mo"), + step("/air-quality/springfield-mo", "done")] + self.assertFailsOn(self.verdict(universal, C["answer"], after=genuine_after()), C["gate"]) + real = [step("/"), step("/search?q=Springfield"), step("/weather/springfield-mo"), + step("/air-quality/springfield-mo", "done")] + self.assertPasses(self.verdict(real, C["answer"], after=genuine_after())) + def test_wrong_answers_fail(self) -> None: for answer, reason in C["wrong"]: with self.subTest(answer=answer): diff --git a/sites/accuweather/verify/tests/test_verify_lib.py b/sites/accuweather/verify/tests/test_verify_lib.py index 7be236073..1dbfc05d5 100644 --- a/sites/accuweather/verify/tests/test_verify_lib.py +++ b/sites/accuweather/verify/tests/test_verify_lib.py @@ -68,6 +68,36 @@ def test_search_surfaces_and_origin(self) -> None: self.assertTrue(L.search_surfaces(traj, "springfield-mo")) self.assertTrue(L.search_surfaces(traj, "springfield-il")) self.assertFalse(L.search_surfaces(traj, "phoenix-az")) + + def test_search_gate_ignores_catalog_wide_tokens(self) -> None: + """``United States`` matches 20/20 locations, so it must not satisfy the + anti-shortcut gate for any of them; a city/region/postal token must.""" + def q(term): + return {"start_url": "http://localhost:41024/", + "steps": [{"url": "http://localhost:41024/search?q=" + term}]} + for slug in ("springfield-mo", "portland-me", "toronto-ca", "london-gb"): + self.assertFalse(L.search_surfaces(q("United+States"), slug), slug) + self.assertFalse(L.search_surfaces(q("states"), slug), slug) + self.assertTrue(L.search_surfaces(q("Springfield"), "springfield-mo")) + self.assertTrue(L.search_surfaces(q("65806"), "springfield-mo")) + self.assertTrue(L.search_surfaces(q("Kingdom"), "london-gb")) + self.assertNotIn("united", L._discriminative_tokens("london-gb")) + self.assertIn("london", L._discriminative_tokens("london-gb")) + + def test_screenshots_reject_stub_sizes(self) -> None: + """A decodable but 1x1 PNG is a placeholder, not page evidence.""" + from _support import make_png + with tempfile.TemporaryDirectory() as d: + root = Path(d) + (root / "screenshots").mkdir() + traj = {"_run_dir": root, "steps": [{"screenshot_before": "step_000.png", "screenshot_after": "step_001.png"}]} + for w, h, expected in ((320, 200, True), (L.MIN_SHOT_WIDTH, L.MIN_SHOT_HEIGHT, True), + (1, 1, False), (L.MIN_SHOT_WIDTH - 1, L.MIN_SHOT_HEIGHT, False), + (L.MIN_SHOT_WIDTH, L.MIN_SHOT_HEIGHT - 1, False)): + for name in ("step_000.png", "step_001.png"): + (root / "screenshots" / name).write_bytes(make_png(w, h)) + ok, evidence = L.screenshots_decode(traj) + self.assertEqual(ok, expected, f"{w}x{h}: {evidence}") self.assertTrue(L.navigated_to_path({"steps": [{"url": "http://127.0.0.1:5000/weather/phoenix-az?x=1"}]}, "/weather/phoenix-az")) self.assertFalse(L.navigated_to_path({"steps": [{"url": "http://example.com/weather/phoenix-az"}]}, "/weather/phoenix-az")) self.assertFalse(L._same_local_origin("http://localhost:41025/", "http://localhost:41024/")) diff --git a/sites/accuweather/verify/verify_lib.py b/sites/accuweather/verify/verify_lib.py index a6f722d79..f0e6749c2 100644 --- a/sites/accuweather/verify/verify_lib.py +++ b/sites/accuweather/verify/verify_lib.py @@ -91,6 +91,10 @@ SEED_USERS = ("alice.j@test.com", "bob.smith@test.com", "carol.w@test.com", "david.b@test.com") PASSWORD = "TestPass123!" +# A real browser screenshot is at least viewport-sized. A decodable 1x1 PNG is a +# stub, not evidence, so the package-identity gate rejects anything this small. +MIN_SHOT_WIDTH, MIN_SHOT_HEIGHT = 200, 150 + # ---------------------------------------------------------------- CLI @dataclass @@ -216,11 +220,19 @@ def _tokens(text: str) -> set[str]: return set(re.findall(r"[a-z0-9]+", normalize_text(text))) +def _discriminative_tokens(slug: str) -> set[str]: + """`slug`'s own field tokens, minus tokens that most of the catalog shares. + ``united`` and ``states`` match every US location, so ``q=United States`` + proves nothing about having found *this* one; ``springfield`` does.""" + mine = _tokens(" ".join(CATALOG[slug])) + catalog = [_tokens(" ".join(fields)) for fields in CATALOG.values()] + return {t for t in mine if sum(1 for other in catalog if t in other) <= len(catalog) / 2} + + def search_surfaces(traj, slug: str) -> bool: """True when some /search?q= visit would list `slug` under the site's own - scoring (any query token hits city / region / country / postal).""" - fields = " ".join(CATALOG[slug]) - field_tokens = _tokens(fields) + scoring, on a token that actually narrows the catalog down to it.""" + field_tokens = _discriminative_tokens(slug) return any(_tokens(q) & field_tokens for q in search_queries(traj)) @@ -295,8 +307,9 @@ def screenshots_decode(traj) -> tuple[bool, str]: w, h = _png_dimensions(path) except Exception as exc: # noqa: BLE001 return False, f"step {i} {key} cannot decode: {exc}" - if w < 1 or h < 1: - return False, f"step {i} {key} is an empty PNG" + if w < MIN_SHOT_WIDTH or h < MIN_SHOT_HEIGHT: + return False, (f"step {i} {key} is {w}x{h}, under the {MIN_SHOT_WIDTH}x{MIN_SHOT_HEIGHT} " + "minimum for a real page screenshot") checked += 1 return True, f"decoded {checked} PNG screenshots" From 5eb5a77458b16e1e6ab200d6f62bd4552358235a Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:25:06 -0700 Subject: [PATCH 7/9] docs(accuweather): record asset hashes, font licence and mirror scope - provenance.json gains a sha256 for all eight captured assets, so a future re-download can be compared objectively. - New site README recording what is deliberately simplified (no JavaScript, decorative radar, formula-generated forecasts), how the seed is made byte-reproducible, and the font notice: Solis-Regular.woff2 ships with SFNT name ID 0 ("(c) Copyright AccuWeather, 2019. All rights reserved.") and name ID 14 preserved verbatim, carries no licence grant, and can be removed by deleting the file and the @font-face rule since the stylesheet already declares the Arial fallback. Adds non-affiliation and trademark notices. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- sites/accuweather/README.md | 73 +++++++++++++++++++++++++++++++ sites/accuweather/provenance.json | 13 +++++- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 sites/accuweather/README.md diff --git a/sites/accuweather/README.md b/sites/accuweather/README.md new file mode 100644 index 000000000..37c1b9476 --- /dev/null +++ b/sites/accuweather/README.md @@ -0,0 +1,73 @@ +# AccuWeather mirror + +An offline Flask mirror of accuweather.com used as a deterministic benchmark +fixture. It serves 20 locations with current conditions, hourly and daily +forecasts, radar, air quality, accounts, saved locations, alert preferences and +a temperature-unit setting. + +## Scope and deliberate simplifications + +- **No JavaScript.** No template loads a script; every interaction is a plain + form or link, so the site degrades perfectly without JS. +- **Radar is decorative.** `/radar/` renders a CSS gradient, not a map + tile or a real radar image. The heading names the city; nothing else on the + page is a fact a task may depend on. +- **Forecasts are generated from the current temperature** (`app.py`, + `seed_database`): today's high is `current - 2`, the hourly peak is 4 PM for + every city, and the lowest daily low is Saturday for every city. This makes + the list-scan pattern identical across cities. It is deterministic and + self-consistent, but it is not realistic per-city weather. +- **Content is synthetic.** No value is a real-world fact, so no task can be + answered from model knowledge. + +## Seed determinism + +`instance_seed/accuweather.db` is rebuilt at image build time (see +`.build-generated-seed` and the Dockerfile step). The four benchmark users' +password hashes are frozen constants in `app.py` +(`BENCHMARK_PASSWORD_HASHES`) because `generate_password_hash` salts randomly, +which would otherwise make the seed a different file on every build. + +The seed is byte-reproducible **within one SQLite runtime**. Across SQLite +versions the file bytes can differ while every row is identical; compare the +row-level catalog fingerprint (`verify/verify_lib.py`, `CATALOG_FINGERPRINT`) +rather than the file's md5 when checking across environments. + +## Assets and provenance + +Per-asset source URLs are recorded in `provenance.json`. The seven SVG weather +and GPS icons are captured from accuweather.com and are fetched from the pinned +Hugging Face asset bundle (`.requires-images`); they are not tracked in git. + +### Font notice + +`static/fonts/Solis-Regular.woff2` (22832 bytes, sha256 +`e23435d0e387ffe2c818e1f500d0e58e7e996251871fad7df54b38404cc3a384`) is the +webfont served by accuweather.com. Its SFNT name table is shipped **verbatim +and unmodified**; in particular: + +- name ID 0 (copyright): `© Copyright AccuWeather, 2019. All rights reserved.` +- name ID 8 (manufacturer): `Type Network` +- name ID 9 (designer): `Laura Meseguer with Dyana Weissman` +- name ID 11 (vendor URL): `http://www.typenetwork.com/` +- name ID 14 (license URL): `http://www.loyalkaspar.com` + +The file carries no name ID 13 (license description) upstream, and no licence +grant accompanies it. It is included only to reproduce the visual proportions +of the original page in an offline research fixture. + +**Non-affiliation.** This mirror is not affiliated with, endorsed by, or +sponsored by AccuWeather, Inc. or Type Network. "AccuWeather" and "RealFeel" +are trademarks of AccuWeather, Inc., used here only to identify the site being +mirrored. + +**To remove the font**, delete `static/fonts/Solis-Regular.woff2` and the +`@font-face` rule at the top of `static/css/site.css`. The stylesheet already +declares the fallback stack `Solis, Arial, sans-serif`, so the page renders in +Arial with no further change. + +## Grading + +Deterministic verifiers for all 20 tasks live in `verify/`; see +`verify/README.md` for the contract and `verify/tests/` for the unit harness +and the real-browser matrix. diff --git a/sites/accuweather/provenance.json b/sites/accuweather/provenance.json index 925ab9067..4ccc53a3c 100644 --- a/sites/accuweather/provenance.json +++ b/sites/accuweather/provenance.json @@ -11,5 +11,16 @@ "static/images/weather-07.svg": "https://www.accuweather.com/images/weathericons/v2a/07.svg", "static/images/weather-12.svg": "https://www.accuweather.com/images/weathericons/v2a/12.svg", "static/images/icon-gps.svg": "https://www.awxcdn.com/adc-assets/images/icons/icon-gps.svg" - } + }, + "sha256": { + "static/fonts/Solis-Regular.woff2": "e23435d0e387ffe2c818e1f500d0e58e7e996251871fad7df54b38404cc3a384", + "static/images/weather-01.svg": "0fcecf04217671da1ac65d6a81386380bef52e89adbcfd83f485de987bbd8709", + "static/images/weather-02.svg": "86e373fd01509dccccfe1fc35c5f3fc7a9fa5a4126424ca9e8fa7a52af75e544", + "static/images/weather-03.svg": "50b3f5fdb992d905624e540a509ebe75bc545af91bff6293f049560bb7e90f5c", + "static/images/weather-06.svg": "481da999b14b7fa9e793cbdf1b15dd51738f06ad8fa6333338c6921af5096c26", + "static/images/weather-07.svg": "db73455f1f3319e1f22815df478bcb6d25c69ae485d0935ac2ec7486b9b267ea", + "static/images/weather-12.svg": "bf6b677e56531a1dd8fb729bc32672c54b88eef5950588e02a0de9152cc7d302", + "static/images/icon-gps.svg": "d3c18b7fe63f48546ee507a1a39024c07655ff1565fb2edf0910c19386e8005c" + }, + "notes": "Font name IDs 0 (copyright) and 14 (license URL) are preserved verbatim; see README.md for the licence and non-affiliation notice. Hashes are of the files as extracted from the pinned Hugging Face bundle." } From e9e0809d495e1ca27872a95a833ab3d64e238fef Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Sat, 12 Sep 2026 21:55:50 -0700 Subject: [PATCH 8/9] fix(accuweather): restore the build-time seed step lost in the main merge accuweather ships no instance_seed: its seed is generated at image build time. Merging the 26-site main took upstream's Dockerfile wholesale, which dropped that RUN step, so websyn_start.sh failed at 'cp -a .../instance_seed' and the container exited 1 before the control plane came up. Step restored verbatim ahead of EXPOSE. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Dockerfile b/Dockerfile index 583bc5572..b7bba958a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -86,6 +86,15 @@ os.makedirs('instance_seed', exist_ok=True); \ shutil.copy2('instance/rotten_tomatoes.db', 'instance_seed/rotten_tomatoes.db'); \ print('Rotten Tomatoes seed DB generated at build time.')" && rm -rf /opt/WebSyn/rotten_tomatoes/instance +# AccuWeather uses genuine captured UI assets and freezes its deterministic seed. +RUN test -n "$(ls -A /opt/WebSyn/accuweather/static/images)" && \ + cd /opt/WebSyn/accuweather && rm -rf instance instance_seed && python3 -c "\ +import app; \ +import os, shutil; \ +os.makedirs('instance_seed', exist_ok=True); \ +shutil.copy2('instance/accuweather.db', 'instance_seed/accuweather.db'); \ +print('AccuWeather seed DB generated at build time.')" && rm -rf /opt/WebSyn/accuweather/instance + EXPOSE 8101 40000-40026 CMD ["/opt/websyn_start.sh"] From 04cdc0582c3f98c108feb7b93abc65eff5eca2f1 Mon Sep 17 00:00:00 2001 From: JeremyJC67 <56396327+JeremyJC67@users.noreply.github.com> Date: Sun, 13 Sep 2026 09:29:59 -0700 Subject: [PATCH 9/9] fix(accuweather): restore the build-time seed step dropped by the main merge again Same loss as before: this site ships no instance_seed and generates it at image build time, but resolving the Dockerfile conflict by taking upstream's copy removes the RUN block, so websyn_start.sh dies on 'cp -a .../instance_seed' and the container exits 1. Restored verbatim. Any future merge of main must re-check the Dockerfile for this block. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AkNTAZMziDodR6uqa6QBtP --- Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Dockerfile b/Dockerfile index c5a2cbab7..60b502b40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -92,6 +92,15 @@ os.makedirs('instance_seed', exist_ok=True); \ shutil.copy2('instance/rotten_tomatoes.db', 'instance_seed/rotten_tomatoes.db'); \ print('Rotten Tomatoes seed DB generated at build time.')" && rm -rf /opt/WebSyn/rotten_tomatoes/instance +# AccuWeather uses genuine captured UI assets and freezes its deterministic seed. +RUN test -n "$(ls -A /opt/WebSyn/accuweather/static/images)" && \ + cd /opt/WebSyn/accuweather && rm -rf instance instance_seed && python3 -c "\ +import app; \ +import os, shutil; \ +os.makedirs('instance_seed', exist_ok=True); \ +shutil.copy2('instance/accuweather.db', 'instance_seed/accuweather.db'); \ +print('AccuWeather seed DB generated at build time.')" && rm -rf /opt/WebSyn/accuweather/instance + EXPOSE 8101 40000-40028 CMD ["/opt/websyn_start.sh"]