diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0c215b..95a5184 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,7 @@ jobs: node-version: "22" cache: npm - run: python scripts/build_site.py --output dist/site + - run: python scripts/build_dataset.py --output dist/casebook - run: npm ci - run: npx playwright install --with-deps chromium - run: npm run test:site @@ -64,6 +65,10 @@ jobs: name: evidence-site path: dist/site/ include-hidden-files: true + - uses: actions/upload-artifact@v4 + with: + name: evidence-casebook + path: dist/casebook/ pages: if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' && github.repository == 'noteflowai/evalarc' needs: [test, docker-audit, site] @@ -109,6 +114,10 @@ jobs: with: name: evidence-site path: dist/site + - uses: actions/download-artifact@v4 + with: + name: evidence-casebook + path: dist/casebook - run: python -m pip install huggingface_hub==1.31.0 - name: Publish and verify public files env: @@ -118,3 +127,4 @@ jobs: run: | test "$(gh api repos/$GITHUB_REPOSITORY/git/ref/heads/main --jq .object.sha)" = "$GITHUB_SHA" python scripts/publish_space.py --bundle dist/site + python scripts/publish_dataset.py --bundle dist/casebook diff --git a/README.md b/README.md index bce6257..85105e9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@
Interactive evidence lab · Web demo · + Filterable casebook · Releases · 简体中文 · Research & papers · @@ -49,6 +50,13 @@ remain inspectable. Configured acceptance is separate from task resolution. [](https://glayguo-evalarc.static.hf.space/#suite) +Prefer tables or Python? The [Hugging Face casebook](https://huggingface.co/datasets/glayguo/evalarc-casebook) +separates 167 audit cases, six repeated attempts and three suite jobs into +filterable configurations, with unchanged source JSON and provenance. +Start with `suite_jobs` to compare `gate_accepted` and `fully_resolved`. +These are scripted public-development records, not a held-out model benchmark. +[Data guide and reproduction](docs/casebook.md). + `evalarc repeat` freezes one candidate, runs fresh attempts on fixed cases, and reports every outcome with per-check pass rates. Runs now record JSONL progress, enforce a total case budget, and save bounded process diagnostics. diff --git a/README.zh-CN.md b/README.zh-CN.md index 9730272..1921117 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -8,6 +8,7 @@ Python 3.11+,Linux 主机,零运行时第三方依赖,MIT 许可证。 [在线交互演示](https://huggingface.co/spaces/glayguo/evalarc) · [网页镜像](https://noteflowai.github.io/evalarc/) · +[可筛选证据数据集](https://huggingface.co/datasets/glayguo/evalarc-casebook) · [版本下载](https://github.com/noteflowai/evalarc/releases) · [English](README.md) · [中文调研与论文分析](docs/research.zh-CN.md) · [架构设计](docs/architecture.md) · [方法说明](docs/methodology.md) · [开发路线](docs/roadmap.md) @@ -41,6 +42,12 @@ v0.5 新增 `evalarc suite`:用 TOML 声明任务、候选、轮次、预算 [](https://glayguo-evalarc.static.hf.space/#suite) +[Hugging Face Casebook](https://huggingface.co/datasets/glayguo/evalarc-casebook) +把 167 条审计用例、6 次重复尝试和 3 项验收作业分别整理为可筛选的表, +保留未经改写的原始 JSON 和版本指纹。可先选择 `suite_jobs`, +对照 `gate_accepted` 与 `fully_resolved`,或用 Python 读取。 +这些是公开开发任务中的脚本对照,不是隐藏模型测试集。详见[数据说明](docs/casebook.md)。 + `evalarc repeat` 固定一份候选快照,在相同场景上重新启动多轮评测, 保存每轮证据并显示逐项通过率与结果波动。同时补齐场景总时间预算、JSONL 进度 和受限进程诊断。详见[重复评测指南](docs/reliability.zh-CN.md)。 diff --git a/docs/casebook.md b/docs/casebook.md new file mode 100644 index 0000000..88f3e87 --- /dev/null +++ b/docs/casebook.md @@ -0,0 +1,61 @@ +# Casebook: filter and reuse the recorded evidence + +The [Hugging Face dataset](https://huggingface.co/datasets/glayguo/evalarc-casebook) +provides a tabular companion to the interactive lab. The default `suite_jobs` +view makes an important distinction inspectable: two jobs share a 93.75% mean +score and zero resolved attempts, but their configured gates disagree. + +| Configuration | Rows | One row represents | +| --- | ---: | --- | +| `suite_jobs` | 3 | A configured job and its gate decision | +| `audit_cases` | 167 | A case execution within one of 17 scripted controls | +| `repetition_attempts` | 6 | A recorded attempt of a frozen support policy | + +The row units differ. Do not add them together as independent trials or average +the parent `evaluation_score` repeated across case rows. Each configuration uses +a `development` split. No training or held-out model test split is claimed. + +```python +from datasets import load_dataset + +jobs = load_dataset("glayguo/evalarc-casebook", "suite_jobs", split="development") +for job in jobs: + if not job["fully_resolved"]: + print(job["id"], job["mean_score"], job["gate_accepted"]) +``` + +Install `datasets` only if using this optional reader. The three JSONL files +are also readable with Python's standard `json` module. EvalArc itself still +has no third-party runtime dependency. + +Every row carries the original file path, JSON Pointer, file hash, source commit, +candidate/grader/cases fingerprints and recorded runtime. The bundle includes +unchanged source JSON, all repeated attempts, suite configuration and JUnit. +`case_json`, `checks_json`, `gate_json`, `decision_json` and `runtime_json` are +JSON strings so unrelated task schemas remain readable in a common table. + +## Reproduce and publish + +From a clean checkout of the commit named in the dataset card: + +```bash +python3 scripts/build_dataset.py --output dist/casebook +``` + +The builder reuses the site's evidence checks, validates each audit evaluation, +recomputes repetitions and suite decisions, and verifies JUnit. It checks row +counts and unique identities, preserves original evidence bytes and writes a +file manifest. An existing output directory is never overwritten. + +CI builds the dataset and site from the same source, retaining both artifacts. +The existing publication job requires all tests, Docker audits and browser +checks to pass. It publishes only the current main commit, uses an expected +Hub parent revision, and verifies every uploaded file without credentials. +A retry can initialize an empty dataset owned by the authenticated user; an +existing unrelated dataset is rejected. + +The source records come from EvalArc 0.2, 0.4 and 0.5; their original versions +and grader hashes are retained. These are authored development fixtures, +not model-generated trajectories or a matched longitudinal benchmark. +See the full [dataset card](../huggingface/DATASET.md) for authorship, +license, provenance and interpretation limits. diff --git a/docs/outreach/huggingface-introduction.md b/docs/outreach/huggingface-introduction.md index 4893794..c31c069 100644 --- a/docs/outreach/huggingface-introduction.md +++ b/docs/outreach/huggingface-introduction.md @@ -3,6 +3,14 @@ graders behind AI-agent evaluations. **Updated for v0.5: same score, different acceptance gate.** +**New data companion:** the +[EvalArc Casebook](https://huggingface.co/datasets/glayguo/evalarc-casebook) +makes the recorded evidence filterable in Hugging Face: 167 audit cases, six +repeated attempts and three suite jobs in separate development configurations. +Start with `suite_jobs` to compare acceptance with full resolution, or load the +JSONL in Python. Every row links to unchanged source JSON and a fixed code +revision. This is a small scripted casebook, not a new model benchmark. + The new suite showcase puts the same frozen defective policy through two explicit rules. Both support jobs score 93.75% and resolve 0/2 attempts. A deliberately permissive gate accepts the partial result; requiring every diff --git a/docs/outreach/launch.md b/docs/outreach/launch.md index 5a2693f..3b35967 100644 --- a/docs/outreach/launch.md +++ b/docs/outreach/launch.md @@ -10,7 +10,9 @@ EvalArc 是面向 AI 智能体的开源评测与评分器审计工具,帮助 和 JUnit 导出:同样是 93.75%、没有完全通过的尝试,宽松规则可允许部分进展, 要求备注检查全部通过的规则则拒绝。页面分别展示任务完成情况和规则决策, 并保留重复评测、版本回归与全部运行证据,适合 PAI 智能体创新场景的评测设计、 -验收验证与技术交流。当前为研究预览版,演示使用脚本对照,尚未给出真实大模型 +验收验证与技术交流。HF Casebook 进一步提供可筛选、可用 Python 读取的公开 +证据表,分别保留 167 条用例、6 次重复尝试和 3 项验收作业及其原始记录。 +当前为研究预览版,演示使用脚本对照,尚未给出真实大模型 性能或强化学习收益结论。 ## English @@ -37,12 +39,18 @@ permissive threshold but fails a gate requiring every notes check. Three jobs, five attempts and the complete configuration remain inspectable. No hosted CI importer or model-provider integration is claimed. +The Hugging Face Casebook makes 167 audit cases, six repeated attempts and three +suite jobs filterable and readable from Python as separate development +configurations. Each row retains a pointer and hash for its original source +record. This is a tabular view of existing evidence, not additional model trials. + ## Entry points - Source: https://github.com/noteflowai/evalarc - Demo: https://huggingface.co/spaces/glayguo/evalarc - Web mirror: https://noteflowai.github.io/evalarc/ - Releases: https://github.com/noteflowai/evalarc/releases +- Data: https://huggingface.co/datasets/glayguo/evalarc-casebook For each community, write a description appropriate to its rules and audience. Disclose maintainer affiliation. Do not present pending editorial submissions diff --git a/huggingface/DATASET.md b/huggingface/DATASET.md new file mode 100644 index 0000000..d81b0c2 --- /dev/null +++ b/huggingface/DATASET.md @@ -0,0 +1,139 @@ +--- +pretty_name: EvalArc Casebook — inspect scores, failures and acceptance gates +license: mit +language: + - en +size_categories: + - n<1K +task_categories: + - other +tags: + - tabular + - agent-evaluation + - software-testing + - reproducibility + - synthetic +configs: + - config_name: suite_jobs + default: true + data_files: + - split: development + path: data/suite_jobs.jsonl + - config_name: audit_cases + data_files: + - split: development + path: data/audit_cases.jsonl + - config_name: repetition_attempts + data_files: + - split: development + path: data/repetition_attempts.jsonl +--- + +# EvalArc Casebook + +**The same 93.75% score can pass one acceptance gate and fail another.** +Inspect the rules, actual failed checks and original Docker records in a +filterable table. This is the data companion to the +[interactive evidence lab](https://huggingface.co/spaces/glayguo/evalarc). + +In the default `suite_jobs` view, compare `support-partial` and +`support-protected`. Both use the same frozen defective policy, score 93.75% +and fully resolve 0/2 attempts. The deliberately permissive rule accepts partial +progress; the rule requiring every notes check rejects it. `gate_accepted` +and `fully_resolved` are separate columns. + +## What is included + +| Configuration | Rows | Unit and purpose | +| --- | ---: | --- | +| `suite_jobs` | 3 | One configured gate per job, backed by five Docker attempts; compare acceptance and full resolution. | +| `audit_cases` | 167 | One case execution per scripted control: 135 coding and 32 support cases across two references and 15 declared faults. | +| `repetition_attempts` | 6 | One recorded attempt of a frozen support policy; three reference and three faulty attempts. | + +Each configuration has a single **`development`** split. These are different +units, so their row counts must not be summed into a number of independent +trials or benchmark examples. `evaluation_score` in the case table is the +parent evaluation's score, repeated for navigation; averaging that column +across case rows would reweight evaluations incorrectly. + +These are saved **scripted controls on public development tasks**, not runs of +a trained language model. Support tickets, identifiers and messages are +synthetic fixtures. No customer data or private logs were collected. + +## Use without the web viewer + +```python +from datasets import load_dataset + +jobs = load_dataset("glayguo/evalarc-casebook", "suite_jobs", split="development") +for job in jobs: + if not job["fully_resolved"]: + print(job["id"], job["mean_score"], job["gate_accepted"]) +``` + +No dataset loading script, candidate execution or model credential is required. +You can also download the three JSONL files and read them with Python's standard +`json` module. The `datasets` package is only needed for the example above. + +## Inspect a row's evidence + +`source_file` points to an unchanged JSON file in this dataset repository. +`source_pointer` is a JSON Pointer locating the exact original object; an empty +pointer means the whole evaluation. `source_sha256` authenticates that file. +`source_url` links the corresponding file at a fixed GitHub commit. + +All configurations preserve candidate, grader and case fingerprints and the +recorded runtime. The case table additionally includes original check outcomes +and `case_json`; this retains available support states and tool traces. +The suite configuration, plan, all five attempts and JUnit are included under +`evidence/examples/suite/`. Repetition summaries and all six attempts are included +under their original example directories. + +The original records come from EvalArc 0.2, 0.4 and 0.5. Their +`recorded_evalarc_version` and grader fingerprints are preserved. Do not treat +the three configurations as matched version comparisons; use the lab's separate +matched comparison for that question. + +Build source: +[`@SOURCE_COMMIT@`](https://github.com/noteflowai/evalarc/tree/@SOURCE_COMMIT@). +[`manifest.json`](manifest.json) lists every exported file hash. + +```bash +git clone https://github.com/noteflowai/evalarc.git +cd evalarc +git checkout @SOURCE_COMMIT@ +python3 scripts/build_dataset.py --output dist/casebook +``` + +The build validates evaluations, recomputes repetitions and suite decisions, +and checks JUnit before exporting. It does not rerun candidates. See the +[methodology](https://github.com/noteflowai/evalarc/blob/@SOURCE_COMMIT@/docs/methodology.md) +for the task contracts and limitations. + +## Intended uses and limits + +Use this small casebook to learn grader auditing, inspect retries and +idempotency, test report readers, and discuss acceptance criteria. Reference +implementations and deliberate faults were authored for these tasks; this is +not an exhaustive collection of possible defects. + +The repeated controls show no observed check variation. They do not establish +independence, a population reliability estimate or performance on unseen tasks. +The dataset does not support model rankings, arbitrary reward-hack resistance +or RL improvement claims. Public development cases should not be presented +as a held-out benchmark. No hosted CI importer was exercised. + +## Authorship and license + +Published by the EvalArc maintainer, with AI-assisted development and writing. +Code, task fixtures and these derived records are MIT-licensed; see +[`LICENSE`](LICENSE). No third-party model weights are included. + +Related maintainer projects: +[Robot Reel](https://huggingface.co/spaces/glayguo/robot-reel) inspects recorded +Physical AI experiments; +[Skills Anywhere](https://huggingface.co/spaces/glayguo/dsh-skills-anywhere) +provides reusable skill discovery and file checks. +The [project collection](https://huggingface.co/collections/glayguo/noteflow-ai-open-source-playgrounds-6aa693c382b0184786eb8856) +groups these independent tools; it does not imply a shared model evaluation +or upstream endorsement. diff --git a/huggingface/README.md b/huggingface/README.md index 737c83e..844da08 100644 --- a/huggingface/README.md +++ b/huggingface/README.md @@ -28,6 +28,12 @@ the original TOML, and JUnit output distinguishing a failed gate from an environment error. Gate acceptance remains separate from full task resolution. A hosted CI importer was not exercised. +**Explore the data as tables:** the +[EvalArc Casebook](https://huggingface.co/datasets/glayguo/evalarc-casebook) +offers three separate configurations for 167 audit cases, six repeated attempts +and three suite jobs. Filter the results or load the JSONL in Python; original +source records and fingerprints accompany every row. + **Every v0.4 attempt remains visible.** Switch between three recorded Docker attempts of the reference and three of the duplicate-write control. The reference resolves 3/3 attempts; the faulty control resolves 0/3 diff --git a/scripts/build_dataset.py b/scripts/build_dataset.py new file mode 100644 index 0000000..c5b33e0 --- /dev/null +++ b/scripts/build_dataset.py @@ -0,0 +1,180 @@ +"""Export the recorded casebook for the Hugging Face Dataset Viewer.""" + +from __future__ import annotations + +import argparse +import json +import shutil +import tempfile +from pathlib import Path + +import build_site + +ROOT = build_site.ROOT +SCHEMA = "evalarc.casebook.v1" +COUNTS = {"audit_cases": 167, "repetition_attempts": 6, "suite_jobs": 3} + + +def compact(value: object) -> str: + return json.dumps(value, sort_keys=True, ensure_ascii=False, separators=(",", ":")) + + +def verify(folder: Path) -> dict: + record = build_site.verify(folder) + if record.get("schema") != SCHEMA or record.get("row_counts") != COUNTS: + raise ValueError("Unexpected casebook schema or row inventory") + for name, expected in COUNTS.items(): + rows = [ + json.loads(line) + for line in (folder / "data" / f"{name}.jsonl").read_text().splitlines() + ] + if len(rows) != expected or len({row["id"] for row in rows}) != expected: + raise ValueError(f"Casebook row inventory differs: {name}") + return record + + +def build(destination: Path) -> dict: + from evalarc.records import read_evaluation, validate_evaluation + + destination = destination.resolve() + if destination.exists(): + raise ValueError("Output already exists; choose a fresh build directory") + # Reuse the site's checks of all repetitions, suite gates and JUnit evidence. + with tempfile.TemporaryDirectory(prefix="evalarc-casebook-") as temporary: + source = build_site.build(Path(temporary) / "site") + rows: dict[str, list[dict]] = {key: [] for key in COUNTS} + inputs: set[Path] = set() + + def provenance(path: Path, pointer: str, evaluation: dict) -> dict: + inputs.add(path) + relative = path.relative_to(ROOT).as_posix() + return { + "source_commit": source["source_commit"], + "source_file": f"evidence/{relative}", + "source_pointer": pointer, + "source_sha256": build_site.sha256(path), + "source_url": f"{build_site.SOURCE}/blob/{source['source_commit']}/{relative}", + "task": evaluation["task"]["id"], + "task_split": evaluation["task"]["split"], + "candidate_sha256": evaluation["candidate_sha256"], + "grader_sha256": evaluation["grader_sha256"], + "cases_sha256": evaluation["cases_sha256"], + "runtime_json": compact(evaluation["runtime"]), + } + + for directory in ("audit", "support-audit"): + path = ROOT / "examples" / directory / "audit.json" + audit = json.loads(path.read_text()) + controls = [("reference", "/reference", audit["reference"], "reference")] + controls.extend( + (item["name"], f"/mutants/{i}/evaluation", item["evaluation"], "declared-fault") + for i, item in enumerate(audit["mutants"]) + ) + for name, pointer, evaluation, kind in controls: + validate_evaluation(evaluation) + for i, case in enumerate(evaluation["cases"]): + rows["audit_cases"].append( + { + "id": f"{evaluation['task']['id']}/{name}/{case['seed']}/{case['case_id']}", + "control": name, + "control_kind": kind, + "case_id": case["case_id"], + "seed": case["seed"], + "case_passed": case["passed"], + "case_status": case["status"], + "evaluation_valid": evaluation["valid"], + "evaluation_resolved": evaluation["resolved"], + "evaluation_score": evaluation["score"], + "recorded_evalarc_version": evaluation["evalarc_version"], + "failed_checks": [ + check for check, passed in case["checks"].items() if passed is False + ], + "checks_json": compact(case["checks"]), + "case_json": compact(case), + **provenance(path, f"{pointer}/cases/{i}", evaluation), + } + ) + for control, directory in (("reference", "repetition"), ("faulty", "repetition-faulty")): + folder = ROOT / "examples" / directory + inputs.add(folder / "repetition.json") + for i, path in enumerate(sorted((folder / "attempts").glob("*/evaluation.json")), 1): + evaluation = read_evaluation(path) + rows["repetition_attempts"].append( + { + "id": f"{control}/{i}", + "control": control, + "attempt": i, + "score": evaluation["score"], + "resolved": evaluation["resolved"], + "valid": evaluation["valid"], + "status": evaluation["status"], + "recorded_evalarc_version": evaluation["evalarc_version"], + **provenance(path, "", evaluation), + } + ) + path = ROOT / "examples" / "suite" / "suite.json" + suite = json.loads(path.read_text()) + for i, job in enumerate(suite["jobs"]): + rows["suite_jobs"].append( + { + "id": job["id"], + "mean_score": job["observed"]["mean_score"], + "gate_accepted": job["decision"]["accepted"], + "fully_resolved": job["fully_resolved"], + "valid": job["decision"]["valid"], + "status": job["status"], + "completed_attempts": job["observed"]["completed_attempts"], + "resolved_attempts": job["observed"]["resolved_attempts"], + "assessed_attempts": job["observed"]["assessed_attempts"], + "invalid_attempts": job["observed"]["invalid_attempts"], + "gate_json": compact(job["gate"]), + "decision_json": compact(job["decision"]), + "recorded_evalarc_version": suite["evalarc_version"], + **provenance(path, f"/jobs/{i}", job), + } + ) + # Keep every suite attempt and its configuration alongside the derived rows. + inputs.update( + path + for path in (ROOT / "examples" / "suite").rglob("*") + if path.is_file() and path.suffix in (".json", ".jsonl", ".xml", ".toml") + ) + if {name: len(values) for name, values in rows.items()} != COUNTS: + raise ValueError("Evidence changed; review the casebook inventory before publishing") + (destination / "data").mkdir(parents=True) + for name, values in rows.items(): + (destination / "data" / f"{name}.jsonl").write_text( + # Preserve presentation order: outcome columns precede long fingerprints. + "".join( + json.dumps(row, ensure_ascii=False, separators=(",", ":")) + "\n" for row in values + ) + ) + for path in sorted(inputs): + target = destination / "evidence" / path.relative_to(ROOT) + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(path, target) + card = (ROOT / "huggingface" / "DATASET.md").read_text() + (destination / "README.md").write_text(card.replace("@SOURCE_COMMIT@", source["source_commit"])) + shutil.copyfile(ROOT / "LICENSE", destination / "LICENSE") + record = { + "schema": SCHEMA, + "source_repository": build_site.SOURCE, + "source_commit": source["source_commit"], + "source_dirty": source["source_dirty"], + "row_counts": COUNTS, + "evidence": "Recorded scripted public-development controls; not model evaluations.", + "files": { + path.relative_to(destination).as_posix(): build_site.sha256(path) + for path in sorted(destination.rglob("*")) + if path.is_file() + }, + } + (destination / "manifest.json").write_text(json.dumps(record, indent=2) + "\n") + return verify(destination) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + print(json.dumps(build(args.output), indent=2)) diff --git a/scripts/publish_dataset.py b/scripts/publish_dataset.py new file mode 100644 index 0000000..206caec --- /dev/null +++ b/scripts/publish_dataset.py @@ -0,0 +1,86 @@ +"""Publish a checked casebook artifact, then verify it without credentials.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path + +from build_dataset import SCHEMA, verify +from build_site import SOURCE + + +def publish(folder: Path, repo_id: str) -> dict: + folder = folder.resolve() + record = verify(folder) + if record["source_dirty"]: + raise ValueError("Commit the source before publishing") + if record["source_commit"] != os.environ.get("GITHUB_SHA", record["source_commit"]): + raise ValueError("Bundle differs from the CI source commit") + from huggingface_hub import DatasetCard, HfApi, hf_hub_download + from huggingface_hub.errors import EntryNotFoundError + + DatasetCard.load(folder / "README.md").validate() + api = HfApi() + identity = api.whoami() + if repo_id.split("/")[0] != identity["name"]: + raise ValueError("Casebook publication requires the authenticated owner's namespace") + if not api.repo_exists(repo_id, repo_type="dataset"): + api.create_repo(repo_id, repo_type="dataset", private=False) + info = api.dataset_info(repo_id) + if info.private or info.gated: + raise ValueError("Existing casebook must already be public and ungated") + previous = set() + try: + old_path = hf_hub_download(repo_id, "manifest.json", repo_type="dataset", revision=info.sha) + except EntryNotFoundError: + # Only bootstrap a new, empty dataset owned by this account. This also + # makes a retry after create_repo safe without overwriting existing data. + files = api.list_repo_files(repo_id, repo_type="dataset", revision=info.sha) + if set(files) - {".gitattributes"}: + raise ValueError("Existing dataset has no casebook ownership manifest") from None + else: + old = json.loads(Path(old_path).read_text()) + if old.get("schema") != SCHEMA or old.get("source_repository") != SOURCE: + raise ValueError("Existing dataset belongs to another source") + previous = set(old["files"]) + for name in previous: + path = Path(name) + if path.is_absolute() or ".." in path.parts or any(c in name for c in "*?["): + raise ValueError("Unsafe path in previous manifest") + allowed = sorted(set(record["files"]) | {"manifest.json"}) + commit = api.upload_folder( + repo_id=repo_id, + repo_type="dataset", + folder_path=folder, + allow_patterns=allowed, + delete_patterns=sorted(previous - set(allowed)) or None, + parent_commit=info.sha, + commit_message="Publish verified EvalArc casebook " + record["source_commit"][:12], + ) + for name in allowed: + remote = Path( + hf_hub_download(repo_id, name, repo_type="dataset", revision=commit.oid, token=False) + ) + if ( + hashlib.sha256(remote.read_bytes()).digest() + != hashlib.sha256((folder / name).read_bytes()).digest() + ): + raise ValueError(f"Anonymous readback mismatch: {name}") + return { + "repo_id": repo_id, + "source_commit": record["source_commit"], + "hub_commit": commit.oid, + "verified_public_files": len(allowed), + "row_counts": record["row_counts"], + } + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--bundle", type=Path, required=True) + parser.add_argument("--repo-id", default="glayguo/evalarc-casebook") + args = parser.parse_args() + print(json.dumps(publish(args.bundle, args.repo_id), indent=2)) diff --git a/site/index.html b/site/index.html index 6cc27e8..053044f 100644 --- a/site/index.html +++ b/site/index.html @@ -58,6 +58,7 @@
Recorded Docker suite: three jobs, five attempts, 31 case executions. JUnit distinguishes a rejected gate from an environment error; a hosted CI importer was not exercised. This is scripted development evidence.
+Prefer a filterable table or Python? Open the Hugging Face Casebook ↗ and compare gate_accepted with fully_resolved. The original records accompany every row.
NEW IN 0.4 / REPEAT ONE FROZEN CANDIDATE
Same cases. Fresh state.
Every attempt stays visible.