"""F4-dev-01 replication script. Reads only data/, writes out/results.json deterministically. Values are extracted by regex from the release text; matched source lines are printed.""" import json, re, hashlib, os SRC_ID = "statcan-daily-lfs-2026-09" SRC = f"data/sources/{SRC_ID}.txt" README = "data/README.md" EXPECTED_SHA = "7ea5fec57c87874ff5ee09a8304927a1aaed5a1837db5312fab9f7a2d5f2d0bb" def wc_l(path): with open(path, "rb") as f: return f.read().count(b"\n") raw = open(SRC, "rb").read() sha = hashlib.sha256(raw).hexdigest() assert sha == EXPECTED_SHA, f"SHA mismatch: {sha}" lines = raw.decode("utf-8").split("\n") # map each line to the most recent '## ' section heading (level-2 only, as required by TASK.md) section_of, cur = [], None for ln in lines: if ln.startswith("## "): cur = ln[3:].strip() section_of.append(cur) def find(pattern): hits = [] for i, ln in enumerate(lines): m = re.search(pattern, ln) if m: hits.append((i + 1, section_of[i], m, ln)) return hits def loc(section): return f'{SRC_ID} | section "{section}"' # (a1) national unemployment rate -- first statement in Highlights ur_hits = find(r"The unemployment rate (?:increased|rose|fell|decreased) \d+\.\d percentage points to (\d+\.\d)%") ur_nat = [h for h in ur_hits if h[1] in ("Highlights", "Unemployment rate increases to 6.5%")] ur_vals = {float(h[2].group(1)) for h in ur_nat} assert len(ur_vals) == 1, ur_vals ur = ur_vals.pop() ur_primary = next(h for h in ur_nat if h[1] == "Highlights") # (a2) national employment change emp_hits = find(r"^(?:In September, e|E)mployment (declined|increased|rose|fell) by ([\d,]+) \(") emp_vals = set() for h in emp_hits: sign = -1 if h[2].group(1) in ("declined", "fell") else 1 emp_vals.add(sign * int(h[2].group(2).replace(",", ""))) assert len(emp_vals) == 1, emp_vals emp = emp_vals.pop() emp_primary = next(h for h in emp_hits if h[1] == "Highlights") print("Matched source lines:") for tag, hs in (("unemployment_rate", ur_nat), ("employment_change", emp_hits)): for (n, sec, m, ln) in hs: print(f" [{tag}] line {n} | section \"{sec}\": {ln}") # (b) assertion: women +0.3 to 5.3%; men ROSE 0.2 to 5.8% w = find(r"core-aged women \(25 to 54 years old\), the unemployment rate (rose|fell) (\d+\.\d) percentage points to (\d+\.\d)%") mn = find(r"unemployment rate for core-aged men (rose|fell) (\d+\.\d) percentage points to (\d+\.\d)%") assert len(w) == 1 and len(mn) == 1 wd, wpp, wlvl = w[0][2].groups() md, mpp, mlvl = mn[0][2].groups() print(f" [assertion women] line {w[0][0]} | section \"{w[0][1]}\": {w[0][3]}") print(f" [assertion men] line {mn[0][0]} | section \"{mn[0][1]}\": {mn[0][3]}") women_ok = (wd == "rose" and wpp == "0.3" and wlvl == "5.3") men_ok = (md == "rose" and mpp == "0.2" and mlvl == "5.8") verdict = "supported" if (women_ok and men_ok) else "not_supported" assert w[0][1] == mn[0][1] reason = (f"The women's element matches (\"rose {wpp} percentage points to {wlvl}%\"), but the release states that " f"\"the unemployment rate for core-aged men {md} {mpp} percentage points to {mlvl}% in the month\", " f"contradicting the claim that it rose." if not men_ok else "Every element of the assertion is stated in the release.") results = { "task_id": "F4-dev-01", "unemployment_rate_sep2026": ur, "employment_change_sep2026_persons": emp, "locators": { "unemployment_rate_sep2026": loc(ur_primary[1]), "employment_change_sep2026_persons": loc(emp_primary[1]), }, "assertion_verdict": verdict, "assertion_locator": loc(w[0][1]), "assertion_reason": reason, "feasibility": "complete", "feasibility_reason": None, "data_issues": [ {"issue": "data/README.md rights note refers to re-set tables ('RESEA Tables 2-3, London Table 3 header') and regression-table rendering that do not exist in this extract (apparently template text from another task).", "handling": "Ignored; irrelevant to the LFS extract, whose SHA-256 matches the README value. No tables are reproduced in the extract."}, {"issue": "Some text (e.g. layoff rate, participation rate, population-aging text box) appears under '## Infographic 2' because chart/infographic titles are rendered as '##' headings; the text-box title is not a '##' heading.", "handling": "Not relevant to the extracted figures, which appear under substantive narrative headings; locators use the nearest preceding '##' heading."}, ], "n_input_rows": {"README.md": wc_l(README), f"{SRC_ID}.txt": wc_l(SRC)}, "n_analysed": 3, "random_seed": None, "baseline_consistency": { "unemployment_rate_statements_agree": True, "n_unemployment_rate_statements": len(ur_nat), "employment_change_statements_agree": True, "n_employment_change_statements": len(emp_hits), "alternate_locator_unemployment_rate": loc("Unemployment rate increases to 6.5%"), "alternate_locator_employment_change": loc("Employment falls for second consecutive month"), }, "source_sha256": sha, } os.makedirs("out", exist_ok=True) with open("out/results.json", "w") as f: json.dump(results, f, indent=2, ensure_ascii=False) f.write("\n") print(f"unemployment_rate_sep2026={ur}; employment_change_sep2026_persons={emp}; assertion_verdict={verdict}")