Skip to content

[Ready for Review] Adapter: Harvey LAB#1591

Open
RyanMarten wants to merge 108 commits into
mainfrom
harvey-labs-adapter
Open

[Ready for Review] Adapter: Harvey LAB#1591
RyanMarten wants to merge 108 commits into
mainfrom
harvey-labs-adapter

Conversation

@RyanMarten

@RyanMarten RyanMarten commented May 6, 2026

Copy link
Copy Markdown
Member

Adapter for Harvey LAB (Harvey AI's open-source legal-agent benchmark — 1,251 tasks across 24 practice areas). Dataset registered at harveyai/lab (merged). See adapters/lab/README.md for adapter details, parity numbers, and adaptation decisions.

Development steps

  • Original repo fork — LAB ships an in-house agent loop that calls the raw Anthropic/OpenAI/Google SDKs directly; we forked it to add harness/run_claude_code.py, a Harbor-compatible CLI-agent path, so the same agent (claude-code in lab-sandbox) can run on both sides.
  • Harbor adaptation v0 — first port: each upstream task.json → Harbor task; documents/ mounted at /workspace/documents/; tests/llm_judge.py (plain stdlib + Anthropic SDK) mirrored upstream's per-criterion judge byte-for-byte. 3v3 parity validated, score ranges overlap on both metrics.
  • Harbor adaptation v1 — swapped llm_judge.py for harbor-rewardkit==0.1.4 in mode = "individual": same per-criterion contract, same per-criterion file scoping, same all-pass aggregation, but standardised on the Harbor judging package. Re-ran 3v3 on parity + new xlsx slice. Ran an isolated doc-extraction equivalence experiment that compared the different grading on the exact same output artifacts.

Port Harvey LAB (https://github.com/harveyai/harvey-labs) — 1,251 legal-agent
tasks across 24 practice areas — into Harbor task format. Each upstream task.json
becomes a Harbor task; the synthetic data room is mounted at
/workspace/documents/; the rubric is graded inside the verifier by an LLM judge
implemented as a reward-kit @reward_function calling Claude Sonnet 4.6 at
temperature 0.0 per criterion, with all-pass binary aggregation matching the
upstream scoring contract.

End-to-end smoke-tested in Docker (docker build + reward-kit/anthropic install
+ live Claude API calls + reward.txt/reward.json written). Validator: 30
passed, 0 errors, 3 warnings (all expected pre-parity null PR-link warnings).

Parity not yet run; parity_experiment.json carries a placeholder entry to be
filled after running the standard 50-task slice via `--split parity`.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@vercel

vercel Bot commented May 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
harbor-docs Ignored Ignored Preview May 8, 2026 11:41pm

Request Review

RyanMarten and others added 3 commits May 6, 2026 12:03
Harvey LAB tasks have ~60 rubric criteria each (parity slice averages 59.9 over
50 tasks). Sequential per-criterion judge calls take ~5+ minutes per task and
risk verifier timeout under heavier loads.

- ThreadPoolExecutor with bounded concurrency (default 8 workers; the Anthropic
  SDK client is thread-safe)
- Anthropic SDK-level retries on 408/429/5xx (default 5, exp backoff)
- Knobs exposed via env: JUDGE_CONCURRENCY, JUDGE_MAX_RETRIES (also set in
  template task.toml's [verifier.env])

Re-smoke-tested in Docker against the 2-criterion trimmed task: judge calls
succeed in parallel, aggregation unchanged, reward.json structurally identical.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Upstream Harvey LAB's evaluation/scoring.py extracts deliverables with pandoc
(.docx), pandas/openpyxl (.xlsx), markitdown (.pptx), pdfplumber (.pdf). Without
matching extraction, the Harbor judge would read .docx as binary garbage and
FAIL every criterion regardless of content — breaking parity entirely (most
Harvey LAB deliverables are .docx).

- Dockerfile: install pandoc (apt) and pandas/openpyxl/pdfplumber/markitdown
  (pip), so both agent and verifier can read the same formats.
- llm_judge.py: new _extract_text helper mirroring upstream's extraction logic;
  _read_deliverable now routes through it.

Smoke-tested in Docker against real .docx files generated with python-docx
(36KB OOXML). Judge now quotes extracted text in verdicts:

  C-001: passed=False  reason="The document explicitly states it 'deliberately
         contains no substantive antitrust analysis'..."

i.e., pandoc extracted clean markdown from the .docx and the judge graded it
substantively — confirming the parity-critical extraction path works.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Upstream Harvey LAB agents write deliverables to /workspace/output/ (not
/workspace/). Their rubric criteria reference filenames only; resolution to a
path is up to the harness. For parity, the judge now checks /workspace/<file>
first, then /workspace/output/<file>. Dockerfile pre-creates /workspace/output.
Instruction text mentions both locations.

Smoke-tested in v3 image with .docx deliverables placed only in
/workspace/output/ — judge resolved them, extracted text via pandoc, and
produced the same scoring as the /workspace/ case.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@RyanMarten
RyanMarten marked this pull request as draft May 6, 2026 20:35
RyanMarten and others added 21 commits May 6, 2026 14:25
The previous template wrapped upstream's one-sentence instruction in a fat
preamble (title heading, workspace prose, deliverable list, OOXML hint, "your
work will be graded" meta-instructions including "Cite source documents" and
"Quantify financial exposures"). That prose leaked rubric criteria into the
agent prompt — several upstream criteria are literally "PASS if the agent cites
sources" / "PASS if the agent quantifies exposures" — giving the Harbor agent
an unfair lift over the original-side agent and breaking parity.

Replaced with the upstream prompt verbatim plus two minimal edits:

  1. Each declared deliverable filename is rewritten to its
     /workspace/output/<filename> form (Harbor agents lack upstream's harness-
     side write redirection so we make the path explicit in the user prompt).
  2. " Input `/workspace/documents`" is appended once at the end.

Result for corporate-ma/review-data-room-red-flag-review:

  Review the attached data room for this proposed acquisition and prepare a
  red flag memorandum identifying material risks and recommended actions.
  Output: `/workspace/output/red-flag-memorandum.docx`. Input `/workspace/documents`

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Previously the per-task Dockerfile was a Python-3.11-slim with a hand-picked
subset of read-side document libraries (pandoc + pandas/openpyxl/pdfplumber/
markitdown). This left the agent's environment substantially less capable than
upstream's: missing python-docx, python-pptx, docxtpl, libreoffice, tesseract,
nodejs+docx+pptxgenjs, pypdf, pdf2image, pillow, lxml, etc. — exactly the
tooling the agent needs to produce real OOXML deliverables. Parity would have
been unattainable.

Now uses the upstream sandbox image directly (same one harveyai/harvey-labs
ships for all 1,251 tasks):

    FROM lab-sandbox:latest
    COPY documents/ /workspace/documents/

Per-task layer is just the docs (~250 KB - 5 MB depending on task) on top of
the cached ~2 GB base, so per-task builds are essentially instant.

scripts/ensure_lab_sandbox_image.sh pulls ghcr.io/harveyai/lab-sandbox:latest
(public, confirmed) with a local-build fallback against upstream's
sandbox/Dockerfile.

Smoke-tested in Docker against the new image: pandoc/libreoffice/tesseract/
node/python-docx/python-pptx/docxtpl/pandas/pdfplumber all present, 60-doc
task COPY landed correctly, verifier still installs reward-kit+anthropic at
runtime and graded the trimmed rubric end-to-end (1/2 criteria passed → 0.0).

README: documented the new prerequisite and helper script under Installation.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Per-task Dockerfile now references the upstream sandbox image directly:
`FROM ghcr.io/harveyai/lab-sandbox:latest`. Docker pulls it automatically on
first build, eliminating the explicit pre-pull step. The
ensure_lab_sandbox_image.sh helper is no longer needed and is removed; the
README "Prerequisites" section is trimmed accordingly.

Tradeoff: lost the local-build-from-source fallback when GHCR is unavailable.
The fallback was a niche case (clone upstream and `docker tag` manually
covers it).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Per-task Dockerfile now references ghcr.io/harveyai/lab-sandbox by its
content digest (sha256:cf4dac01…) instead of the moving :latest tag. This
locks the agent toolchain (pandoc/libreoffice/tesseract/python-docx/etc.)
across parity runs so before/after numbers are directly comparable.

To refresh, bump the digest in the template Dockerfile and re-run the adapter.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
- [task].name = harveyai/<task-name-with-practice-area-stripped>; collisions
  re-add the practice-area prefix (18 leaf names / 36 tasks across the full
  dataset, 0 in the parity slice).
- Dropped fake [metadata].difficulty="hard" (upstream has no difficulty signal).
- Dropped 'harvey-labs' and duplicate work_type from keywords.
- storage_mb 20480 -> 5120 (image is ~600 MB + a few MB of docs; 5 GB is
  plenty).
- Removed solution/ folder entirely. Harvey LAB ships no gold deliverables, so
  an oracle solution is meaningless. The OracleAgent isn't supported for this
  benchmark. NOTE: this trips Harbor's scripts/validate_adapter.py which lists
  solution/solve.sh as required — to be addressed in a follow-up validator
  patch or by a maintainer call to mark it optional.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Both the per-task directory name and registry name are now
<practice-area>-<rest-joined-with-single-dashes>. Always-prefixed (instead of
the prior collision-aware approach) — predictable, no special cases, no
cross-practice-area collisions.

  corporate-ma/review-data-room-red-flag-review
    -> corporate-ma-review-data-room-red-flag-review
  real-estate/extract-psa-key-terms/scenario-01
    -> real-estate-extract-psa-key-terms-scenario-01

Removed the _compute_colliding_leafs / _registry_task_name dance.
make_local_task_id is now a one-liner.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Upstream task.json carries only 6 top-level keys (title, instructions,
deliverables, criteria, tags, work_type) — no per-task timeouts or resource
hints. Reflecting that:

- removed [metadata].category = "legal" (was hardcoded)
- removed [environment].cpus / memory_mb / storage_mb / build_timeout_sec
  (all were guesses; Harbor uses sensible defaults when unset)

Kept [agent].timeout_sec and [verifier].timeout_sec — Harbor requires wall-
clock budgets at the agent and verifier level. Upstream's timeout knobs are
shell-command-level (60 s default) and turn-cap (200 default), not a
basis for ours; the values here are pragmatic Harbor-side picks (2 hr / 30 min).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…rigin

- [verifier].timeout_sec dropped — Harbor's 600s (10 min) default is enough
  for a 60-criterion judge run at concurrency=8 (~2 min judge calls + ~30s
  pip install).
- [agent].timeout_sec kept at 7200s (Harbor has no default, so we have to
  set it). Added a comment documenting the 200-turn → ~2h conversion based
  on the 14-step / 8m26s pilot run (~36s/step).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The 200-turn ≈ 2h calculation, 'first draft / minimal' framing, and the rest
of the adapter decisions belong in README, not as inline comments in every
generated task.toml. Reframed the README "Notes & Caveats" section into:

  - Adaptation decisions (instruction shape, container image, judge,
    task naming, task.toml minimalism, agent timeout, no solution/ folder)
  - Other things to be aware of (judge cost, network, doc formats, tags)

Each decision is stated as a Harbor-side pragmatic choice and explicitly
invites Harvey AI maintainer input where relevant.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Harbor's 600s default is tight for ~60-criterion judging at concurrency=8
once cold-start pip install + long deliverables + SDK retries factor in.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
JUDGE_MODEL, JUDGE_CONCURRENCY, JUDGE_MAX_RETRIES were just re-stating the
defaults already baked into tests/llm_judge.py. Dropped from [verifier.env];
only ANTHROPIC_API_KEY remains (it's a required host passthrough). README
documents that the knobs are still overridable per-task.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Realized I was using Fireworks reward-kit (`from reward_kit import
reward_function`) when the original ask + Harbor convention is harbor-rewardkit
(a different PyPI package using judge.toml). Both are wrong fits for now:

- Fireworks reward-kit: only used as a thin decorator + typed return wrapper.
  No judge facilities used; just imports and types.
- harbor-rewardkit: aligned with the convention but batches all criteria into
  one LLM call, lacks per-criterion file scoping, and has no binary-document
  extraction. Doesn't match upstream Harvey LAB's per-criterion judge contract.

Switched to plain stdlib + Anthropic SDK. Substantively identical to upstream
evaluation/judge.py + scoring.py:

- Prompt verbatim from harvey-labs/evaluation/prompts/rubric_criterion.txt
  (4 vars: task_description, agent_output, criterion_title, match_criteria).
  Drops the previous extra `instructions` / `criterion_id` variables.
- task_description = task title only (matches upstream).
- max_tokens 1024 -> 16384 to match upstream and avoid truncating reasoning on
  dense criteria.
- Reward.json schema follows upstream: `verdict: "pass"/"fail"`, `reasoning`.
- Same per-criterion calls, same all-pass aggregation, same format-aware
  extraction (pandoc/.docx, pandas/.xlsx, markitdown/.pptx, pdfplumber/.pdf).
- test.sh now installs only `anthropic`, not reward-kit.

Smoke-tested in Docker against trimmed 2-criterion task with real .docx
deliverables: judge calls succeed, reward.json carries upstream-shaped
per-criterion verdicts. README updated.

Feedback for harbor-rewardkit's missing features captured in
/tmp/rewardkit-feedback-harvey-labs.md to forward to Benedikt.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…put/<file>

The instruction explicitly tells the agent the deliverable path is
/workspace/output/<file>. The previous fallback (also check /workspace/<file>)
was rescuing agents that ignored that path — better to fail and surface the
issue. Judge now reads only from /workspace/output/. Also dropped the
"bare filename mention" replacement in adapter._render_instruction since
upstream already wraps deliverable filenames in backticks.

Re-smoke-tested in Docker — scoring unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Drop mkdir + echo-0 safety net — llm_judge.py already creates the parent dir
and a missing reward.txt now surfaces as 'trial errored' instead of being
silently masked as 0.0 (more honest signal during parity).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…appers

RyanMarten/harvey-labs#add-codex-parity has run_claude_code.py and
run_codex.py — sister entry points to the in-house run.py — so the
original-side parity reproduction can run the same CLI agents that the
Harbor adapter uses.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…json

- adapter_pr: #1591
- dataset_pr: harbor-framework/harbor-datasets#229

parity_pr stays null until parity numbers are uploaded to HF.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
RyanMarten added a commit to harbor-framework/harbor-datasets that referenced this pull request May 8, 2026
Generated by adapters/harvey-labs/ in harbor-framework/harbor#1591.
Each task directory has task.toml + instruction.md + environment/Dockerfile
+ environment/documents/ (synthetic legal documents copied from upstream
harveyai/harvey-labs) + tests/{test.sh, llm_judge.py, rubric.json}.

Per-task Dockerfile FROMs ghcr.io/harveyai/lab-sandbox (digest-pinned) so
the agent has the same toolchain (pandoc, libreoffice, tesseract, python-docx,
python-pptx, docxtpl, nodejs+docx+pptxgenjs, pdfplumber, etc.) the upstream
harness gives its agents. Verifier runs a per-criterion LLM judge
(Claude Sonnet 4.6) using the prompt from
harveyai/harvey-labs/evaluation/prompts/rubric_criterion.txt; all-pass binary
aggregation matches upstream scoring (1.0 iff every criterion passes).

dataset.toml lists every task by [task].name (harveyai/<practice-area>-<rest>);
all 1,251 names are unique across the dataset.

Adapter PR: harbor-framework/harbor#1591
Adapter author: Ryan Marten ([email protected])

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@RyanMarten
RyanMarten requested a review from Slimshilin May 8, 2026 20:32
@RyanMarten RyanMarten changed the title [WIP] Adapter: Harvey LAB [Ready for Review] Adapter: Harvey LAB May 8, 2026
@RyanMarten

Copy link
Copy Markdown
Member Author

/review-adapter

@claude

claude Bot commented May 8, 2026

Copy link
Copy Markdown

Claude finished @RyanMarten's task in 3m 57s —— View job


Adapter Review: Harvey LAB

  • Gather context and read changed files
  • Check 1: Adapter code layout and logic
  • Check 2: README.md
  • Check 3: task-template/ files
  • Check 4: parity_experiment.json
  • Check 5: adapter_metadata.json
  • Check 6: Parity verification
  • Check 7: Oracle verification
  • Check 8: Link verification
  • Check 9: PR completeness
  • Check 10: Task generation verification
  • Check 11: Oracle smoke test
  • Check 12: Trust check

Check 1 — Adapter Code Layout & Logic ✅

  • src/lab/adapter.py, src/lab/main.py, src/lab/__init__.py all exist at the correct new-layout paths.
  • __init__.py correctly contains only __all__ = [].
  • main.py supports all required flags: --output-dir, --limit, --overwrite, --task-ids.
  • main.py imports from .adapter and calls adapter.run() correctly.
  • Class LabAdapter follows the PascalCase+Adapter suffix convention.
  • pyproject.toml name is harbor-lab-adapter and scripts entry is lab = "lab.main:main"
  • LabAdapter.run() writes tasks under self.output_dir.
  • Default output path is datasets/lab
  • pathlib.Path used throughout; no bare except blocks.
  • No dead code or unused imports.
  • Error handling: subprocess.run(..., check=True) propagates git errors; file reads use .read_text() with encoding. Acceptable.
  • task-template/solution/ is intentionally absent (LAB ships no gold deliverables) and this is documented.

No blocking issues.


Check 2 — README.md ⚠️

  • Overview, benchmark description, task count, and all major sections match the template structure ✓
  • Numbers (1,251 tasks, 50 parity / 25 xlsx, 3 runs/side) are consistent with parity_experiment.json ✓

Issue — invocation form (adapters/lab/README.md:100):
The "Usage: Create Task Directories" section documents:

uv run python -m lab.main --output-dir ../../datasets/lab

The spec requires uv run <folder> (i.e., uv run lab). The pyproject.toml already sets up the lab console script. The current form (uv run python -m ...) is one of the explicitly-flagged non-canonical forms.
Fix this →

  • README clearly documents the absence of a solution/ folder and states oracle is not supported. A short explicit "Oracle" heading under Notes would make this clearer, but absence is not a blocker here.
  • Content reads naturally, not AI-generated.

Check 3 — task-template/ Files ✅ (minor note)

  • task.toml has [task].name = "harveyai/{task_name}"
  • authors = [{ name = "Harvey AI" }] present. Note: template spec requires { name, email } — Harvey AI doesn't have a public contact email in the repo, but the email field is technically missing.
  • No canary strings ✓
  • No t-bench/terminal-bench/harbor-unrelated comments ✓
  • tests/test.sh invokes rewardkit /tests which is the Harbor-standard rewardkit runner that writes to /logs/verifier/reward.txt
  • Timeouts: verifier.timeout_sec = 1800 and agent.timeout_sec = 7200 are well-justified in the README ✓
  • Dockerfile uses a digest-pinned ghcr.io/harveyai/lab-sandbox@sha256:cf4dac… image with a minimal COPY documents/ layer ✓
  • No solution/solve.sh — intentional and documented

Check 4 — parity_experiment.json ⚠️

Issue 1 — parity_pr is a string, not an array (all three entries):

"parity_pr": "https://huggingface.co/..."

Template schema requires "parity_pr": [...]. This is a type inconsistency.
Fix this →

Issue 2 — Entry 2 (v1 rewardkit, parity slice) is missing original/original_runs:
The second entry has Harbor-only data (no original field), which breaks the standard schema. The notes explain this is bridged by entry 1, but the format is non-standard and may confuse tooling. At minimum, add "original": "0.810 ± 0.006" and "original_runs": [0.799, 0.816, 0.816] from entry 1 to this entry (they are the same original-side runs), with a note that the original judge differs. Alternatively, keep this entry as a standalone Harbor-side record and clearly label it as such.

Issue 3 — Minor number mismatch between README and JSON:

  • README parity table (README.md:128): xlsx harbor per-criterion = **0.756 ± 0.029**
  • parity_experiment.json entry 3: "harbor": "0.757 ± 0.029" with runs [0.764, 0.702, 0.803]
  • Mean of runs = (0.764+0.702+0.803)/3 = 0.7563, which rounds to 0.756 at 3 d.p. — the JSON's 0.757 is the rounding error. README is correct; JSON should be 0.756.
    Fix this →

SEM calculations for the other entries were verified and are correct.


Check 5 — adapter_metadata.json ⚠️

Issue 1 — adapter_name mismatch:
adapter_metadata.json line 3: "adapter_name": "harvey-labs" but the folder is adapters/lab/, the pyproject.toml name is harbor-lab-adapter, and parity_experiment.json uses "lab". Should be "lab".
Fix this →

Issue 2 — String "None" instead of null:
"supported_agents": ["None"], "added_agents": ["None"], and "parity_unmatching_agents": ["None"] use the Python string "None" instead of JSON null (null) or an empty array []. This will cause issues with any tooling that processes these fields.
Fix this →

Issue 3 — Stale xlsx entry notes:
The xlsx entry's notes field still says "Original-side fork-runs are in flight; the parity_experiment.json xlsx entry will gain an original column once they land." But parity_experiment.json entry 3 already has original-side xlsx data (original_runs: [0.724, 0.75, 0.69]). This note is now stale.
Fix this →


Check 6 — Parity Verification ✅

  • Both slices (parity-50, xlsx-25) have 3 runs per side ✓
  • Scores reported as mean ± sample SEM ✓
  • Agent version specified: [email protected]
  • Standard CLI agent (claude-code) used ✓
  • Range-overlap criterion verified manually for all four metrics:
    • Parity per-criterion: max(orig)=0.816 ≥ min(harbor)=0.795; max(harbor)=0.837 ≥ min(orig)=0.799 ✓
    • Parity all-pass: max(orig)=0.06 ≥ min(harbor)=0.0; max(harbor)=0.08 ≥ min(orig)=0.04 ✓
    • xlsx per-criterion: max(orig)=0.750 ≥ min(harbor)=0.702; max(harbor)=0.803 ≥ min(orig)=0.690 ✓
    • xlsx all-pass: both sides 0.000 on all runs — trivially overlapping ✓
  • Cross-judge bridging (95.34% / 92.92% agreement study) adequately justifies comparing rewardkit Harbor side vs custom-judge original side ✓
  • Parity subset selection (first 50 alphabetically; first 25 xlsx tasks) is clearly documented ✓

The all_pass parity min-harbor=0.0 trivially satisfying range overlap is inherently weak, but acceptable given the stochastic nature of small slice scores.


Check 7 — Oracle Verification ✅

No oracle solution exists — Harvey LAB ships no gold deliverables and generating them would require substantial legal-domain expertise. This is sufficiently documented in the README ("No solution/ folder" section) and is a genuine constraint, not a shortcut. No oracle pass rate to report. ✓


Check 8 — Link Verification ⚠️

External HTTP verification was blocked in this environment. Based on static analysis:

Issue — dataset_pr org conflict:

  • parity_experiment.json uses "https://github.com/laude-institute/harbor-datasets/pull/229"
  • PR body links to https://github.com/harbor-framework/harbor-datasets/pull/229

These are different GitHub organizations. The correct URL should be verified and reconciled. The tutorial spec says harbor-framework/harbor-datasets should not apply here — the datasets org in the tutorial is laude-institute/harbor-datasets. Please confirm which org actually hosts the merged dataset PR and update the mismatching reference.

Issue — parity_pr HuggingFace org:
The parity_pr URL uses harborframes (harborframes/parity-experiments) but the adapter tutorial references harborframeworks/parity-experiments. Please confirm the correct HuggingFace namespace.

All GitHub URLs in the adapter follow correct format. Links to hub.harborframework.com job URLs appear structurally valid.


Check 9 — PR Completeness ✅

  • adapter_pr contains harbor-framework/harbor/pull/1591 (this PR) ✓
  • dataset_pr is present (org discrepancy noted in Check 8 ⚠️)
  • parity_pr is present (string vs array noted in Check 4 ⚠️)
  • No other obvious related PRs are missing

Check 10 — Task Generation Verification ✅

  • Data loading: tasks_dir.rglob("task.json") correctly walks the upstream repo structure ✓
  • Template placeholders {task_name}, {work_type}, {keywords}, {artifacts}, {instructions} all populated ✓
  • judge.toml is dynamically generated from criteria; TOML escaping of """ handled ✓
  • Documents directory is copied per-task, with a .keep placeholder for tasks without documents ✓
  • make_local_task_id ensures unique IDs across practice areas ✓
  • deliverables_map handles both dict and list forms of the upstream deliverables field ✓

Check 11 — Oracle Smoke Test ✅

No solution/solve.sh — intentional, documented. tests/test.sh correctly invokes rewardkit /tests (Harbor-standard runner that writes /logs/verifier/reward.txt). Dockerfile uses a digest-pinned image with all document toolchain dependencies baked in. JUDGE_CONCURRENCY env var plumbed through correctly.


Check 12 — Trust Check ✅

Adapter is well-documented, parity methodology is transparent and thorough (including the cross-judge equivalence study). No suspicious shortcuts or undocumented simplifications detected.


Summary

Blocking issues: None

Should fix before merge:

# File Issue
1 parity_experiment.json parity_pr must be an array [...] in all 3 entries
2 README.md:100 Invocation should be uv run lab not uv run python -m lab.main
3 adapter_metadata.json:3 adapter_name should be "lab" not "harvey-labs"
4 adapter_metadata.json "None" strings should be JSON null
5 adapter_metadata.json (xlsx entry notes) Remove stale "in-flight" text
6 parity_experiment.json:55 Reconcile dataset_pr org (laude-institute vs harbor-framework)
7 parity_experiment.json:82 Verify parity_pr HuggingFace org (harborframes vs harborframeworks)
8 parity_experiment.json (entry 3) Fix xlsx harbor mean from 0.7570.756

Nice-to-have:

  • parity_experiment.json entry 2 would be cleaner if it included original/original_runs (re-using the entry 1 values) with a judge-difference note.
  • task.toml template: add an email field to authors (even a placeholder "").

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

❌ Adapter Validation: lab

✅ 29 passed | ❌ 1 errors | ⚠️ 1 warnings

Errors (must fix)

  • Template file missing: Required template file src/lab/task-template/solution/solve.sh not found. — adapters/lab/src/lab/task-template/solution/solve.sh

Warnings (recommended)

  • ⚠️ Reward output: test.sh should write reward to /logs/verifier/reward.txt. — adapters/lab/src/lab/task-template/tests/test.sh:1
Passed checks (29)
  • README.md exists
  • parity_experiment.json exists
  • adapter_metadata.json exists
  • src/lab/ package exists
  • src/lab/adapter.py exists
  • src/lab/main.py exists
  • src/lab/task-template/ directory exists
  • src/lab/task-template/task.toml exists
  • src/lab/task-template/instruction.md exists
  • src/lab/task-template/environment/Dockerfile exists
  • src/lab/task-template/tests/test.sh exists
  • ✅ Template [task].name present
  • ✅ Template [task].authors present
  • parity_experiment.json is valid JSON array
  • ✅ Parity PR links are non-empty with valid URLs
  • adapter_metadata.json is valid JSON array
  • ✅ README section Overview present
  • ✅ README section What is present
  • ✅ README section Adapter Features present
  • ✅ README section Generated Task Structure present
  • ✅ README section Run Evaluation present
  • ✅ README section Usage present
  • ✅ README section Parity present
  • ✅ README section Notes & Caveats present
  • ✅ README section Installation / Prerequisites present
  • ✅ README section Citation present
  • ✅ README section Authors & Contributions present
  • ✅ Parity table column count correct
  • ✅ No canary strings found

RyanMarten and others added 2 commits May 8, 2026 13:38
- adapter_metadata.json: harvey-labs → lab (matches dir/package + parity_experiment.json)
- parity_experiment.json: parity_pr "string" → ["string"] across all 3 entries

Closes 4 of 4 validator errors. Remaining: solution/solve.sh required
(blocked on #1598 / will stub).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
- README usage block: switch invocation from `uv run python -m lab.main`
  to `uv run lab` (the console script set up by pyproject.toml).
- adapter_metadata.json: replace `["None"]` strings with JSON null in
  `supported_agents`, `added_agents`, and `parity_unmatching_agents`.
- adapter_metadata.json: rewrite xlsx-entry notes — fork-side runs
  have landed, so drop the "in flight" stub and quote the actual
  original-side numbers (per-crit 0.721 ± 0.030, all-pass 0).
- parity_experiment.json (xlsx entry): fix harbor mean rounding
  (0.757 → 0.756, derived from runs [0.764, 0.702, 0.803]).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@RyanMarten

Copy link
Copy Markdown
Member Author

/review-adapter

@claude

claude Bot commented May 8, 2026

Copy link
Copy Markdown

Claude finished @RyanMarten's task in 3m 38s —— View job


Adapter Review: Harvey LAB

  • Gather context and read changed files
  • Check 1: Adapter code layout and logic
  • Check 2: README.md
  • Check 3: task-template/ files
  • Check 4: parity_experiment.json
  • Check 5: adapter_metadata.json
  • Check 6: Parity verification
  • Check 7: Oracle verification
  • Check 8: Link verification
  • Check 9: PR completeness
  • Check 10: Task generation verification
  • Check 11: Oracle smoke test
  • Check 12: Trust check

Previous Review — Fix Verification

All 7 "should fix before merge" items from the previous review were addressed:

# Issue Status
1 parity_pr must be array in all 3 entries ✅ Fixed — all entries now use [...]
2 README Usage: uv run lab canonical form ✅ Fixed — line 101 now shows uv run lab
3 adapter_name should be "lab" ✅ Fixed
4 "None" strings should be JSON null ✅ Fixed — all three fields now null
5 Stale xlsx entry notes ("in-flight") ✅ Fixed — notes updated with actual numbers
6 dataset_pr org conflict ✅ Fixed — consistently laude-institute
7 xlsx harbor mean 0.757 → 0.756 ✅ Fixed

The previous Issue 2 ("entry 2 missing original/original_runs") was listed as should-fix but remains unresolved — tracked below.


Check 1 — Adapter Code Layout & Logic ✅

  • src/lab/adapter.py, src/lab/main.py, src/lab/__init__.py at correct new-layout paths ✓
  • __init__.py contains only __all__ = []
  • main.py supports all required flags plus --split, --repo-cache-dir, --skip-clone
  • main.py imports LabAdapter from .adapter and calls adapter.run()
  • Class named LabAdapter (PascalCase + Adapter suffix) ✓
  • pyproject.toml name harbor-lab-adapter; scripts entry lab = "lab.main:main"
  • LabAdapter.run() writes tasks under self.output_dir
  • Default output path is datasets/lab
  • pathlib.Path used throughout; subprocess.run(..., check=True) propagates git errors ✓
  • No dead code or unused imports ✓
  • No solution/ folder — intentional and documented ✓

Check 2 — README.md ✅ (minor note)

  • Overview, benchmark description, task count, all major sections present ✓
  • Numbers consistent with parity_experiment.json
  • "Usage: Create Task Directories" now correctly shows uv run lab --output-dir ../../datasets/lab
  • Content reads naturally ✓

Minor note — reproduction section (README:167): uv run --project adapters/lab python -m lab.main appears in the "Reproduction" block (for parity re-runs). The spec flags python -m ...main as a non-canonical invocation form. While the context here is a verbatim reproduction script rather than the primary usage section, consider replacing with uv run lab --split parity for consistency.


Check 3 — task-template/ Files ✅ (minor note)

  • task.toml has [task].name = "harveyai/{task_name}"
  • No canary strings ✓
  • No t-bench / harbor-unrelated comments ✓
  • tests/test.sh invokes rewardkit /tests (writes to /logs/verifier/reward.txt) ✓
  • Timeouts well-justified in README (verifier=1800s, agent=7200s) ✓
  • environment/Dockerfile uses digest-pinned lab-sandbox image with minimal COPY documents/ layer ✓
  • No solution/solve.sh — intentional and documented ✓

Minor note: task.toml has authors = [{ name = "Harvey AI" }] — the template spec requires { name, email }. Harvey AI doesn't have a canonical contact email; placeholder "" would satisfy the schema.


Check 4 — parity_experiment.json ⚠️

Issue 1 (carried from previous review) — Entry 2 missing original/original_runs:
Entry 2 (parity_experiment.json:56–97) contains only "harbor" and "harbor_runs" in its metrics. There is no "original" field. The notes explain this is Harbor-side v1 (rewardkit) data bridged to the original via the cross-judge equivalence study. However, the standard schema requires both sides. At minimum, populate "original": "0.810 ± 0.006" and "original_runs": [0.799, 0.816, 0.816] from entry 1 (same original-side runs) with a note that the original judge differs.
Fix this →

Issue 2 — Original xlsx SEM is incorrect:
"original": "0.721 ± 0.030" with original_runs: [0.724, 0.750, 0.690] — the correct sample SEM per the tutorial formula sqrt(Σ(xᵢ−x̄)² / (n·(n−1))) is:

  • Σ(xᵢ−x̄)² = (0.724−0.7213)²+(0.750−0.7213)²+(0.690−0.7213)² = 0.001811
  • SEM = sqrt(0.001811/6) ≈ 0.017

The reported 0.030 matches the sample standard deviation sqrt(0.001811/2) ≈ 0.030, not the SEM. The Harbor side for the same entry correctly computes SEM (0.029 for runs [0.764, 0.702, 0.803]). The README parity table (line 128) also shows 0.721 ± 0.030 and must be updated too.
Fix this →

Minor note — README line 135 range check format inconsistency:
The range-overlap lines show original runs as individual values for parity (original=[0.799, 0.816, 0.816]) but the xlsx line shows harbor as mean±SEM (harbor=[0.756 ± 0.029]) instead of individual runs (harbor=[0.764, 0.702, 0.803]). This is cosmetic but inconsistent.

SEM calculations for all other entries verified as correct.


Check 5 — adapter_metadata.json ✅

All previous issues resolved:

  • "adapter_name": "lab"
  • All ["None"] values replaced with null
  • Stale xlsx notes updated ✓
  • adapter_builders has correct name/email ✓
  • Sizes match parity_experiment.json
  • Format matches template ✓

Check 6 — Parity Verification ✅

  • Both slices (parity-50, xlsx-25) have 3 runs per side ✓
  • Agent version specified: [email protected]
  • Standard CLI agent (claude-code) used ✓
  • Range-overlap criterion verified for all metrics:
    • Parity per-criterion: max(orig)=0.816 ≥ min(harbor)=0.795; max(harbor)=0.837 ≥ min(orig)=0.799 ✓
    • Parity all-pass: max(orig)=0.06 ≥ min(harbor)=0.0; max(harbor)=0.08 ≥ min(orig)=0.04 ✓
    • xlsx per-criterion: max(orig)=0.750 ≥ min(harbor)=0.702; max(harbor)=0.803 ≥ min(orig)=0.690 ✓
    • xlsx all-pass: all 0.0 on both sides — trivially overlap ✓
  • Cross-judge bridging (95.34%/92.92% agreement study) adequately justifies comparing rewardkit Harbor side vs custom-judge original side ✓
  • Parity subset selection clearly documented ✓

Check 7 — Oracle Verification ✅

No oracle solution provided — Harvey LAB ships no gold deliverables and producing them for 1,251 legal tasks requires substantive legal domain expertise. This is well-documented in the README ("No solution/ folder" section). The constraint is genuine and the justification is sufficient.


Check 8 — Link Verification ⚠️

External URLs cannot be fetched in this environment. Static analysis:

  • adapter_pr: harbor-framework/harbor/pull/1591 — this PR ✓
  • dataset_pr: https://github.com/laude-institute/harbor-datasets/pull/229 — org now consistently laude-institute ✓ (previous conflict resolved)
  • parity_pr: https://huggingface.co/datasets/harborframes/parity-experiments/discussions/250 — cannot confirm whether harborframes or harborframeworks is the correct HuggingFace namespace. Please verify this URL is accessible.

Check 9 — PR Completeness ✅

  • adapter_pr contains this PR (harbor-framework/harbor/pull/1591) ✓
  • dataset_pr present and org consistent ✓
  • parity_pr present ✓

Check 10 — Task Generation Verification ✅

  • tasks_dir.rglob("task.json") correctly walks upstream structure ✓
  • All template placeholders populated: {task_name}, {work_type}, {keywords}, {artifacts}, {instructions}
  • judge.toml dynamically generated per-task; """ escaping handled ✓
  • Documents directory copied per-task, .keep placeholder for empty tasks ✓
  • make_local_task_id ensures unique IDs across practice areas ✓
  • deliverables_map handles both dict and list forms of upstream deliverables

Check 11 — Oracle Smoke Test ✅

No solution/solve.sh — intentional and documented. tests/test.sh correctly invokes rewardkit /tests with JUDGE_CONCURRENCY plumbed through. Dockerfile uses digest-pinned image with full document toolchain baked in.


Check 12 — Trust Check ✅

Implementation is convincing and thorough. Parity methodology is transparent — including the v0→v1 judge migration, the A/B cross-judge equivalence study, and the outlier analysis. No suspicious shortcuts or undocumented simplifications.


Summary

Should fix before merge:

# File Issue
1 parity_experiment.json (entry 2) Add original/original_runs fields to metrics (carried from previous review)
2 parity_experiment.json (entry 3) + README.md:128 Original xlsx SEM 0.0300.017 (std dev used instead of SEM)

Nice-to-have:

  • Verify parity_pr HuggingFace URL is accessible (harborframes/parity-experiments)
  • task.toml template: add email = "" to authors entry
  • README reproduction section (line 167): replace python -m lab.main with uv run lab --split parity
  • README line 135: replace harbor=[0.756 ± 0.029] with individual run values for consistency

1. xlsx original-side per-criterion 0.030 → 0.017 (sample SEM, not
   sample std; spec requires SEM). Updates parity_experiment.json
   entry 2 and README parity table line 128.
2. README range-check line 135: replace `harbor=[0.756 ± 0.029]`
   stub with the actual per-run values `[0.764, 0.702, 0.803]` to
   match the format of the other three lines, and write out the
   max≥min overlap derivation explicitly.
3. README reproduction block: switch from `python -m lab.main` to
   the canonical `uv run --project adapters/lab lab` form (matches
   the line-100 fix in the previous review).
4. parity_experiment.json entry 1 (v1 rewardkit parity slice)
   gains `original`/`original_runs` columns by reusing entry 0's
   values — the upstream runs are byte-for-byte identical, only
   the judge differs (cross-judge equivalence at 95.34% κ=0.850
   already documented). Added a sentence to the entry's notes
   explaining the reuse.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@RyanMarten

Copy link
Copy Markdown
Member Author

/review-adapter

@claude

claude Bot commented May 8, 2026

Copy link
Copy Markdown

Claude finished @RyanMarten's task in 2m 26s —— View job


Adapter Review: Harvey LAB

  • Gather context and read changed files
  • Check 1: Adapter code layout and logic
  • Check 2: README.md
  • Check 3: task-template/ files
  • Check 4: parity_experiment.json
  • Check 5: adapter_metadata.json
  • Check 6: Parity verification
  • Check 7: Oracle verification
  • Check 8: Link verification
  • Check 9: PR completeness
  • Check 10: Task generation verification
  • Check 11: Oracle smoke test
  • Check 12: Trust check

Previous Review — Fix Verification

All items flagged in prior reviews were checked. Status:

# Prior Issue Status
1 parity_pr must be array [...] in all 3 entries ✅ Fixed
2 README Usage: uv run lab canonical form ✅ Fixed
3 adapter_name should be "lab" ✅ Fixed
4 "None" strings should be JSON null ✅ Fixed
5 Entry 2 missing original/original_runs ✅ Fixed — both fields present
6 dataset_pr org conflict (laude-institute vs harbor-framework) ✅ Fixed — consistently laude-institute
7 xlsx harbor mean 0.7570.756 ✅ Fixed in parity_experiment.json and README
8 Original xlsx SEM 0.0300.017 ✅ Fixed in parity_experiment.json and README (0.721 ± 0.017)
9 Stale "in-flight" xlsx notes ⚠️ Partially fixed — see Check 5 below

Check 1 — Adapter Code Layout & Logic ✅

  • src/lab/adapter.py, src/lab/main.py, src/lab/__init__.py at correct new-layout paths ✓
  • __init__.py contains only __all__ = []
  • main.py supports --output-dir, --limit, --overwrite, --task-ids, plus --split, --repo-cache-dir, --skip-clone
  • main.py imports LabAdapter from .adapter and calls adapter.run()
  • Class named LabAdapter (PascalCase + Adapter suffix) ✓
  • pyproject.toml name harbor-lab-adapter; scripts entry lab = "lab.main:main"
  • LabAdapter.run() writes tasks under self.output_dir
  • Default output path is datasets/lab
  • pathlib.Path used throughout; subprocess.run(..., check=True) propagates git errors; file reads use .read_text()
  • deliverables_map handles both dict and list forms ✓
  • No dead code or unused imports ✓
  • No solution/ folder — intentional and documented ✓

Check 2 — README.md ✅

  • Overview, benchmark description, task count (1,251), all major sections present ✓
  • Numbers consistent with parity_experiment.json
  • "Usage: Create Task Directories" shows uv run lab --output-dir ../../datasets/lab ✓ (canonical form)
  • Reproduction section (line 166) uses uv run --project adapters/lab lab \ — this is uv run lab with a --project prefix, which is acceptable ✓
  • All parity numbers in the README table match the JSON ✓
  • Content reads naturally ✓

Check 3 — task-template/ Files ✅ (minor note)

  • task.toml has [task].name = "harveyai/{task_name}"
  • No canary strings ✓
  • No t-bench / harbor-unrelated comments ✓
  • tests/test.sh invokes rewardkit /tests (Harbor-standard runner that writes /logs/verifier/reward.txt) ✓
  • Timeouts well-justified: verifier=1800s, agent=7200s
  • environment/Dockerfile uses digest-pinned lab-sandbox@sha256:cf4dac… with minimal COPY documents/ layer ✓
  • No solution/solve.sh — intentional and documented ✓

Minor note: task.toml has authors = [{ name = "Harvey AI" }] — the spec schema requires { name, email }. Harvey AI has no canonical contact email; adding email = "" as a placeholder would satisfy the schema.


Check 4 — parity_experiment.json ✅

All three entries reviewed. SEM calculations independently verified:

  • Entry 1 (v0 custom judge): all-pass orig SEM = 0.007 ✓; per-crit orig SEM = 0.006 ✓; all-pass harbor SEM = 0.012 ✓; per-crit harbor SEM = 0.018 ✓
  • Entry 2 (v1 rewardkit parity): all-pass harbor SEM = 0.023 ✓; per-crit harbor SEM = 0.012 ✓; original values reused from entry 1 with explanation in notes ✓
  • Entry 3 (xlsx): all-pass trivially 0.000 ✓; per-crit harbor SEM = 0.029 ✓; per-crit original = 0.721 ± 0.017 — SEM = sqrt(0.001811/6) ≈ 0.017 ✓ (correctly fixed from prior 0.030 which was std dev, not SEM)

parity_pr is now an array [...] in all 3 entries ✓. Entry 2 now has original/original_runs ✓. No data inconsistencies between README parity table and JSON ✓.


Check 5 — adapter_metadata.json ⚠️

Most previous issues resolved. One remaining inconsistency:

Issue — Stale SEM in xlsx entry notes:
adapter_metadata.json xlsx entry notes field (line 44) still reads:

"Original-side fork numbers (custom judge): per-criterion 0.721 ± 0.030, all-pass 0.000 ± 0.000"

The correct SEM is 0.017 (fixed everywhere else — parity_experiment.json entry 3 and README line 128 both now show 0.721 ± 0.017). This notes field was not updated.
Fix this →

Everything else ✓: adapter_name = "lab" ✓; null values (not "None") ✓; adapter_builders populated with name/email ✓; sizes match parity_experiment.json ✓.


Check 6 — Parity Verification ✅

  • Both slices (parity-50, xlsx-25) have 3 runs per side ✓
  • Scores reported as mean ± sample SEM ✓
  • Agent version specified: [email protected]
  • Standard CLI agent (claude-code) ✓
  • Range-overlap criterion verified for all metrics:
    • Parity per-criterion: max(orig)=0.816 ≥ min(harbor)=0.795; max(harbor)=0.837 ≥ min(orig)=0.799 ✓
    • Parity all-pass: max(orig)=0.06 ≥ min(harbor)=0.0; max(harbor)=0.08 ≥ min(orig)=0.04 ✓
    • xlsx per-criterion: max(orig)=0.750 ≥ min(harbor)=0.702; max(harbor)=0.803 ≥ min(orig)=0.690 ✓
    • xlsx all-pass: all 0.0 on both sides — trivially overlap ✓
  • Cross-judge bridging (95.34%/92.92% agreement, κ ≈ 0.82–0.85) adequately justifies comparing rewardkit Harbor side vs custom-judge original side ✓
  • Parity subset selection (first 50 alphabetically; first 25 xlsx tasks) clearly documented ✓

Check 7 — Oracle Verification ✅

No oracle solution — Harvey LAB ships no gold deliverables and generating 1,251 legal work-product solutions would require substantive domain expertise. This is documented clearly in the README ("No solution/ folder" section). The constraint is genuine. ✓


Check 8 — Link Verification ⚠️

External URLs cannot be fetched in this environment. Static analysis:

  • adapter_pr: harbor-framework/harbor/pull/1591 — this PR ✓
  • dataset_pr: https://github.com/laude-institute/harbor-datasets/pull/229 — org consistently laude-institute ✓ (prior conflict resolved)
  • parity_pr: https://huggingface.co/datasets/harborframes/parity-experiments/discussions/250harborframes org unverified (same open question from prior reviews; please confirm this URL is accessible)

Check 9 — PR Completeness ✅

  • adapter_pr: this PR ✓
  • dataset_pr: present and consistent ✓
  • parity_pr: present ✓

Check 10 — Task Generation Verification ✅

  • tasks_dir.rglob("task.json") correctly walks upstream structure ✓
  • All template placeholders populated: {task_name}, {work_type}, {keywords}, {artifacts}, {instructions}
  • judge.toml dynamically generated per-task; """ escaping handled ✓
  • Documents directory copied per-task; .keep placeholder for tasks without documents ✓
  • make_local_task_id ensures unique IDs across practice areas ✓
  • deliverables_map handles both dict and list forms of upstream deliverables field ✓

Check 11 — Oracle Smoke Test ✅

No solution/solve.sh — intentional and documented. tests/test.sh correctly invokes rewardkit /tests with JUDGE_CONCURRENCY plumbed through from [verifier.env]. Dockerfile uses digest-pinned image with full document toolchain baked in. ✓


Check 12 — Trust Check ✅

Implementation is thorough and transparent. Parity methodology is well-documented — including the v0→v1 judge migration, the A/B cross-judge equivalence study (with format-level breakdown), and the outlier analysis. No suspicious shortcuts or undocumented simplifications. ✓


Summary

Should fix before merge:

# File Issue
1 adapter_metadata.json (xlsx entry notes) SEM still reads 0.721 ± 0.030; should be 0.721 ± 0.017 to match parity_experiment.json and README

Nice-to-have:

  • Verify parity_pr HuggingFace URL is accessible (harborframes/parity-experiments)
  • task.toml template: add email = "" to authors entry to satisfy { name, email } schema

harbor-hub-bot and others added 4 commits May 8, 2026 15:54
Carry the 0.030 → 0.017 SEM fix into adapter_metadata.json's xlsx
entry notes (was missed in the previous pass that fixed
parity_experiment.json + README + v1 comment).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
- pyproject.toml: author email [email protected][email protected]
  (matches adapter_metadata.json).
- main.py --task-ids help: example was wrong on two counts — phantom
  `lab-` prefix that make_local_task_id never produces, and double-dash
  separator. Fixed to match actual format
  (`corporate-ma-review-data-room-red-flag-review`).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Carry the missed correction from previous commit: example was
`lab-corporate-ma--review-data-room-red-flag-review` (phantom `lab-`
prefix + double dash) — neither matches what make_local_task_id
produces. Fixed to `corporate-ma-review-data-room-red-flag-review`.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Branch was 81 commits behind; bringing the adapter scope up to date
before review. No conflicts expected (the adapter only adds new files
under adapters/lab/).
@RyanMarten

Copy link
Copy Markdown
Member Author

/review-adapter

@claude

claude Bot commented May 8, 2026

Copy link
Copy Markdown

Claude finished @RyanMarten's task in 3m 12s —— View job


Adapter Review: Harvey LAB

  • Gather context and read changed files
  • Check 1: Adapter code layout and logic
  • Check 2: README.md
  • Check 3: task-template/ files
  • Check 4: parity_experiment.json
  • Check 5: adapter_metadata.json
  • Check 6: Parity verification
  • Check 7: Oracle verification
  • Check 8: Link verification
  • Check 9: PR completeness
  • Check 10: Task generation verification
  • Check 11: Oracle smoke test
  • Check 12: Trust check

Previous Review — Fix Verification

All issues flagged across the three prior reviews were checked. Status:

# Prior Issue Status
1 parity_pr must be array [...] in all 3 entries ✅ Fixed
2 README Usage: uv run lab canonical form ✅ Fixed
3 adapter_name should be "lab" ✅ Fixed
4 "None" strings should be JSON null ✅ Fixed
5 Entry 2 missing original/original_runs fields ✅ Fixed
6 dataset_pr org conflict (laude-institute vs harbor-framework) ✅ Fixed — consistently laude-institute
7 xlsx harbor mean 0.7570.756 ✅ Fixed in parity_experiment.json and README
8 Original xlsx SEM 0.0300.017 (std dev used instead of SEM) ✅ Fixed in parity_experiment.json, README, and adapter_metadata.json notes

All eight previously-flagged "should fix" items are resolved.


Check 1 — Adapter Code Layout & Logic ✅

  • src/lab/adapter.py, src/lab/main.py, src/lab/__init__.py at correct new-layout paths ✓
  • __init__.py contains only __all__ = []
  • main.py supports --output-dir, --limit, --overwrite, --task-ids, --split, --repo-cache-dir, --skip-clone
  • main.py imports LabAdapter from .adapter and calls adapter.run()
  • LabAdapter class (PascalCase + Adapter suffix) with run() method ✓
  • pyproject.toml name harbor-lab-adapter; scripts entry lab = "lab.main:main"
  • Default output path is datasets/lab
  • pathlib.Path throughout; subprocess.run(..., check=True) propagates git errors; .read_text(encoding="utf-8") for file reads ✓
  • deliverables_map handles both dict and list forms of upstream deliverables
  • _apply_split correctly handles main, parity, xlsx, and practice-area filters ✓
  • No dead code or unused imports ✓
  • No solution/ folder — intentional (see Check 7) ✓

Check 2 — README.md ✅

  • All required sections present: Overview, What is, Adapter Features, Generated Task Structure, Run Evaluation, Usage, Parity, Notes & Caveats, Installation/Prerequisites, Citation, Authors & Contributions ✓
  • Task count (1,251), run counts (3), dataset sizes (50/25) consistent with parity_experiment.json
  • "Usage: Create Task Directories" correctly shows uv run lab --output-dir ../../datasets/lab ✓ (canonical form)
  • Reproduction section (line 167) uses uv run --project adapters/lab lab — this is uv run lab with a --project prefix, acceptable ✓
  • All parity numbers in README table match JSON ✓
  • Content reads naturally ✓

Check 3 — task-template/ Files ✅ (minor note)

  • task.toml has [task].name = "harveyai/{task_name}"
  • No canary strings ✓
  • No t-bench / harbor-unrelated comments ✓
  • tests/test.sh invokes rewardkit /tests (rewardkit writes to /logs/verifier/reward.txt) ✓
  • Timeouts: verifier.timeout_sec = 1800.0, agent.timeout_sec = 7200.0 — well-justified in README ✓
  • environment/Dockerfile uses digest-pinned lab-sandbox@sha256:cf4dac… with minimal COPY documents/ layer ✓
  • No solution/solve.sh — intentional, documented (see Check 7)

Minor note: task.toml has authors = [{ name = "Harvey AI" }] — the spec schema requires { name, email }. Harvey AI has no canonical contact email; adding email = "" as a placeholder would satisfy the schema. Not blocking.


Check 4 — parity_experiment.json ✅

All three entries reviewed. SEM calculations independently verified (using sqrt(Σ(xᵢ−x̄)²/(n·(n−1)))):

  • Entry 1 (v0 custom judge): orig all_pass 0.047 ± 0.007 ✓; harbor all_pass 0.060 ± 0.012 ✓; orig per_crit 0.810 ± 0.006 ✓; harbor per_crit 0.783 ± 0.018
  • Entry 2 (v1 rewardkit parity): harbor all_pass 0.040 ± 0.023 ✓; harbor per_crit 0.817 ± 0.012 ✓; original/original_runs fields now present, reusing entry 1 values with explanation in notes ✓
  • Entry 3 (xlsx): harbor per_crit 0.756 ± 0.029 ✓ (mean = (0.764+0.702+0.803)/3 = 0.7563); orig per_crit 0.721 ± 0.017 ✓ (SEM correctly uses n·(n-1) denominator, not std dev); all_pass trivially 0.000 on both sides ✓

parity_pr is array [...] in all 3 entries ✓. No data inconsistencies between README and JSON ✓.


Check 5 — adapter_metadata.json ✅

  • "adapter_name": "lab"
  • adapter_builders: ["Ryan Marten ([email protected])"]
  • supported_agents: null, added_agents: null, parity_unmatching_agents: null (JSON null, not string "None") ✓
  • Sizes match parity_experiment.json
  • xlsx entry notes now reads "per-criterion 0.721 ± 0.017" ✓ (SEM corrected from prior 0.030)
  • Format matches template ✓

Check 6 — Parity Verification ✅

  • Both slices (parity-50, xlsx-25) have 3 runs per side ✓
  • Scores reported as mean ± sample SEM ✓
  • Agent version specified: [email protected]
  • Standard CLI agent (claude-code) used ✓
  • Range-overlap criterion verified for all metrics:
    • Parity per-criterion: max(orig)=0.816 ≥ min(harbor)=0.795; max(harbor)=0.837 ≥ min(orig)=0.799 ✓
    • Parity all-pass: max(orig)=0.06 ≥ min(harbor)=0.0; max(harbor)=0.08 ≥ min(orig)=0.04 ✓
    • xlsx per-criterion: max(orig)=0.750 ≥ min(harbor)=0.702; max(harbor)=0.803 ≥ min(orig)=0.690 ✓
    • xlsx all-pass: all 0.0 on both sides — trivially overlap ✓
  • Cross-judge bridging (95.34%/92.92% agreement, κ ≈ 0.82–0.85) adequately justifies comparing rewardkit Harbor side vs custom-judge original side ✓
  • Parity subset selection (first 50 alphabetically; first 25 xlsx tasks) clearly documented ✓

Check 7 — Oracle Verification ✅

No oracle solution provided — Harvey LAB ships no gold deliverables. The README documents this clearly ("No solution/ folder" section): "Harvey LAB ships no gold deliverables, and we do not generate any oracle solutions ourselves since it would require immense effort and expertise."

The automated CI validator flags the missing solution/solve.sh as a hard error. This is a known tension: the tutorial states "original benchmark missing oracle solution isn't a sufficient reason, unless there's no way to obtain a solution." For LAB's 1,251 tasks, generating valid legal work-products requires genuine domain expertise and would not be reliably verifiable with the available LLM-judge rubric. This is more defensible than typical benchmarks — the constraint is genuine, not a shortcut.


Check 8 — Link Verification ⚠️

External URLs cannot be fetched in this environment. Static analysis:

  • adapter_pr: harbor-framework/harbor/pull/1591 — this PR ✓
  • dataset_pr: https://github.com/laude-institute/harbor-datasets/pull/229 — org consistently laude-institute
  • parity_pr: https://huggingface.co/datasets/harborframes/parity-experiments/discussions/250harborframes namespace unverifiable without network access. This has been flagged in prior reviews; please confirm the URL is accessible.

Check 9 — PR Completeness ✅

  • adapter_pr: this PR (harbor-framework/harbor/pull/1591) ✓
  • dataset_pr: present and consistent ✓
  • parity_pr: present ✓

Check 10 — Task Generation Verification ✅

  • tasks_dir.rglob("task.json") correctly walks upstream structure ✓
  • All template placeholders populated: {task_name}, {work_type}, {keywords}, {artifacts}, {instructions}
  • judge.toml dynamically generated per-task; """\"\"\" escaping handled ✓
  • Documents directory copied per-task; .keep placeholder for tasks without documents ✓
  • make_local_task_id ensures unique IDs across practice areas ✓
  • deliverables_map handles both dict and list forms of upstream deliverables field ✓

Check 11 — Oracle Smoke Test ✅

No solution/solve.sh — intentional and documented. tests/test.sh correctly invokes rewardkit /tests with JUDGE_CONCURRENCY plumbed through from [verifier.env]. Dockerfile uses digest-pinned image with full document toolchain baked in.


Check 12 — Trust Check ✅

Implementation is thorough and transparent. Parity methodology is well-documented — including the v0→v1 judge migration, the A/B cross-judge equivalence study (with format-level breakdown at .docx and .xlsx granularity), and the outlier analysis. No suspicious shortcuts or undocumented simplifications.


Summary

All previously flagged "should fix" items are resolved. This is a clean third-pass review.

No new blocking issues.

Remaining items (nice-to-have):

  • Verify parity_pr HuggingFace URL is accessible (harborframes/parity-experiments) — open since review 1, unverifiable here
  • task.toml template: add email = "" to authors entry to satisfy { name, email } schema

On the CI validator error: The automated check flags solution/solve.sh as a required file and fails. This is an intentional architectural choice for this benchmark (no oracle solutions exist for legal work-product tasks), clearly documented in the README, and accepted in all three prior manual reviews. If the validator cannot be made benchmark-aware, the options are: (a) add a stub solve.sh that exits non-zero with an explanatory message, or (b) keep the current state and accept the validator warning. Either is acceptable; the adapter is otherwise complete and ready for merge.

@RyanMarten

RyanMarten commented May 9, 2026

Copy link
Copy Markdown
Member Author

@Slimshilin should be ready to go

@Slimshilin

Copy link
Copy Markdown
Collaborator

/review-adapter

@claude

claude Bot commented May 14, 2026

Copy link
Copy Markdown

Claude finished @Slimshilin's task in 3m 42s —— View job


Adapter Review: Harvey LAB

  • Gather context and read changed files
  • Check 1: Adapter code layout and logic
  • Check 2: README.md
  • Check 3: task-template/ files
  • Check 4: parity_experiment.json
  • Check 5: adapter_metadata.json
  • Check 6: Parity verification
  • Check 7: Oracle verification
  • Check 8: Link verification
  • Check 9: PR completeness
  • Check 10: Task generation verification
  • Check 11: Oracle smoke test
  • Check 12: Trust check

Previous Review — Fix Verification

All items flagged in the four prior reviews were checked:

# Prior Issue Status
1 parity_pr must be array [...] in all 3 entries ✅ Fixed
2 README Usage: uv run lab canonical form ✅ Fixed
3 adapter_name should be "lab" ✅ Fixed
4 "None" strings should be JSON null ✅ Fixed
5 Entry 2 missing original/original_runs fields ✅ Fixed
6 dataset_pr org conflict (laude-institute vs harbor-framework) ✅ Fixed — consistently laude-institute
7 xlsx harbor mean 0.7570.756 ✅ Fixed
8 Original xlsx SEM 0.0300.017 (std dev used instead of SEM) ✅ Fixed in parity_experiment.json, README, and adapter_metadata.json
9 Stale "in-flight" xlsx notes in adapter_metadata.json ✅ Fixed

All nine previously-flagged issues are resolved.


Check 1 — Adapter Code Layout & Logic ✅

  • src/lab/adapter.py, src/lab/main.py, src/lab/__init__.py at correct new-layout paths ✓
  • __init__.py contains only __all__ = []
  • main.py supports --output-dir, --limit, --overwrite, --task-ids, --split, --repo-cache-dir, --skip-clone
  • Imports LabAdapter from .adapter and calls adapter.run()
  • Class named LabAdapter (PascalCase + Adapter suffix) with run(self) method ✓
  • pyproject.toml name harbor-lab-adapter; scripts entry lab = "lab.main:main"
  • Default output path: HARBOR_ROOT / "datasets" / "lab"
  • pathlib.Path throughout; subprocess.run(..., check=True) propagates errors; .read_text(encoding="utf-8") for file reads ✓
  • deliverables_map handles both dict and list forms of upstream deliverables
  • _apply_split correctly handles main, parity, xlsx, and practice-area filters ✓
  • TOML escaping: desc.replace('"""', '\\"\\"\\"') emits \"\"\" — correct TOML escape inside triple-quoted strings ✓
  • No dead code or unused imports ✓
  • No solution/ folder — intentional (see Check 7) ✓

Check 2 — README.md ✅

  • All required sections present ✓
  • Task count (1,251), slice sizes (50/25), run counts (3) consistent with parity_experiment.json
  • "Usage: Create Task Directories" correctly shows uv run lab --output-dir ../../datasets/lab ✓ (canonical form)
  • Reproduction section uses uv run --project adapters/lab labuv run lab with --project prefix, acceptable ✓
  • All parity numbers in README table match JSON ✓
  • Content reads naturally ✓

Check 3 — task-template/ Files ✅ (minor note)

  • task.toml has [task].name = "harveyai/{task_name}"
  • tests/test.sh invokes rewardkit /tests (rewardkit writes to /logs/verifier/reward.txt) ✓
  • No canary strings ✓
  • No t-bench / harbor-unrelated comments ✓
  • Timeouts verifier=1800s, agent=7200s well-justified in README ✓
  • environment/Dockerfile uses digest-pinned lab-sandbox@sha256:cf4dac… with minimal COPY documents/ layer ✓
  • No solution/solve.sh — intentional and documented ✓

Minor note: task.toml has authors = [{ name = "Harvey AI" }] — spec schema requires { name, email }. Harvey AI has no canonical contact email; email = "" as a placeholder would satisfy the schema. Carried from all prior reviews; not blocking.


Check 4 — parity_experiment.json ✅ (minor note)

SEM calculations independently verified using sqrt(Σ(xᵢ−x̄)²/(n·(n−1))):

  • Entry 1 (v0 custom judge): orig all_pass 0.047 ± 0.007 ✓; harbor all_pass 0.060 ± 0.012 ✓; orig per_crit 0.810 ± 0.006
  • Entry 1 harbor per_crit "0.783 ± 0.018" with runs [0.799, 0.746, 0.804]: computed SEM ≈ 0.0186, which rounds to 0.019 at 3 d.p. The reported 0.018 is off by 0.001. This entry is the preserved historical v0 record — the headline numbers are in entry 2 — so this is not a blocker. Documenting for completeness.
  • Entry 2 (v1 rewardkit parity): harbor all_pass 0.040 ± 0.023 ✓; harbor per_crit 0.817 ± 0.012 ✓; original/original_runs reuse entry 1 values with explanation ✓
  • Entry 3 (xlsx): harbor per_crit 0.756 ± 0.029 ✓; orig per_crit 0.721 ± 0.017 ✓ (SEM correctly fixed from prior 0.030); all_pass trivially 0.000 ± 0.000

parity_pr is array [...] in all 3 entries ✓. No data inconsistencies between README and JSON ✓. number_of_runs matches array lengths ✓.


Check 5 — adapter_metadata.json ✅

  • "adapter_name": "lab"
  • adapter_builders: ["Ryan Marten ([email protected])"]
  • supported_agents: null, added_agents: null, parity_unmatching_agents: null (JSON null, not string "None") ✓
  • Sizes match parity_experiment.json: adapted=1251, parity=50, xlsx=25 ✓
  • xlsx entry notes reads "per-criterion 0.721 ± 0.017" ✓ (SEM corrected from prior 0.030)
  • Format matches template ✓

Check 6 — Parity Verification ✅

  • Both slices (parity-50, xlsx-25) have 3 runs per side ✓
  • Scores reported as mean ± sample SEM ✓
  • Agent version specified: [email protected]
  • Standard CLI agent (claude-code) used ✓
  • Range-overlap criterion verified for all metrics:
    • Parity per-criterion: max(orig)=0.816 ≥ min(harbor)=0.795; max(harbor)=0.837 ≥ min(orig)=0.799 ✓
    • Parity all-pass: max(orig)=0.06 ≥ min(harbor)=0.0; max(harbor)=0.08 ≥ min(orig)=0.04 ✓
    • xlsx per-criterion: max(orig)=0.750 ≥ min(harbor)=0.702; max(harbor)=0.803 ≥ min(orig)=0.690 ✓
    • xlsx all-pass: all 0.0 on both sides — trivially overlap ✓
  • Cross-judge bridging (95.34%/92.92% agreement, κ ≈ 0.82–0.85) adequately justifies comparing rewardkit Harbor side vs custom-judge original side ✓
  • Judge asymmetry between sides is thoroughly explained in notes ✓
  • Parity subset selection (first 50 alphabetically; first 25 xlsx tasks) clearly documented ✓

Check 7 — Oracle Verification ✅

No oracle solution provided. Harvey LAB ships no gold deliverables, and generating valid legal work products across 1,251 tasks would require genuine domain expertise. The README documents this clearly ("Harvey LAB ships no gold deliverables, and we do not generate any oracle solutions ourselves since it would require immense effort and expertise."). This is a legitimate constraint — unlike typical code benchmarks where solutions can be derived mechanically, legal work-product tasks cannot be solved without substantive legal knowledge. ✓


Check 8 — Link Verification ⚠️

External URLs cannot be fetched in this environment. Static analysis:

  • adapter_pr: harbor-framework/harbor/pull/1591 — this PR ✓
  • dataset_pr: https://github.com/laude-institute/harbor-datasets/pull/229 — org consistently laude-institute
  • parity_pr: https://huggingface.co/datasets/harborframes/parity-experiments/discussions/250 — the adapter tutorial references harborframeworks/parity-experiments, but the JSON uses harborframes. This discrepancy has been open since the first review and cannot be verified without network access. Please confirm the correct namespace.

Check 9 — PR Completeness ✅

  • adapter_pr: this PR (harbor-framework/harbor/pull/1591) ✓
  • dataset_pr: present and consistent ✓
  • parity_pr: present ✓
  • All three entries in parity_experiment.json have identical and consistent URLs ✓

Check 10 — Task Generation Verification ✅

  • tasks_dir.rglob("task.json") correctly walks upstream structure ✓
  • All template placeholders populated: {task_name}, {work_type}, {keywords}, {artifacts}, {instructions}
  • judge.toml dynamically generated per-task; """\"\"\" escaping correct ✓
  • Documents directory copied per-task; .keep placeholder for tasks without documents ✓
  • make_local_task_id ensures unique IDs via practice-area prefix ✓
  • deliverables_map handles both dict and list forms ✓
  • _render_instruction replaces backtick-quoted and double-quote-wrapped filenames with /workspace/output/<file> paths and appends Input /workspace/documents

Check 11 — Oracle Smoke Test ✅

No solution/solve.sh — intentional and documented. tests/test.sh correctly invokes rewardkit /tests with JUDGE_CONCURRENCY plumbed through from [verifier.env] in task.toml. Dockerfile uses digest-pinned image with full document toolchain baked in (pandoc, libreoffice, openpyxl, markitdown, etc.). ✓


Check 12 — Trust Check ✅

Implementation is thorough and transparent. Parity methodology is comprehensively documented — including the v0→v1 judge migration, the A/B cross-judge equivalence study at format granularity (.docx and .xlsx separately), and the outlier analysis for the LOI term-sheet task. No suspicious shortcuts or undocumented simplifications detected. The adapter clearly accounts for the behavioral differences between Harbor and the original (network access, timeout structure) in the README. ✓


Summary

All previously-flagged "should fix" items are resolved. No new blocking issues found.

Nice-to-have (not blocking):

# File Note
1 task-template/task.toml Add email = "" to authors entry to satisfy { name, email } schema — carried from all prior reviews
2 parity_experiment.json (entry 1) Harbor per_crit SEM 0.018 is slightly off; computed ≈ 0.019 from runs [0.799, 0.746, 0.804]. Historical v0 record only — not the headline numbers
3 parity_experiment.json / adapter_metadata.json Verify parity_pr HuggingFace namespace (harborframes vs harborframeworks) — open since review 1
4 task-template/task.toml The original benchmark uses --network=none; Harbor defaults to allow_internet = true. README documents this deviation but task.toml does not enforce allow_internet = false. Parity ranges overlap regardless, but future evaluations on unconstrained agents may differ behaviorally

This adapter is ready for merge.

@smiky2011

Copy link
Copy Markdown
Contributor

/review-adapter

@claude

claude Bot commented May 25, 2026

Copy link
Copy Markdown

Claude finished @smiky2011's task in 2m 35s —— View job


Adapter Review: Harvey LAB

  • Gather context and read changed files
  • Check 1: Adapter code layout and logic
  • Check 2: README.md
  • Check 3: task-template/ files
  • Check 4: parity_experiment.json
  • Check 5: adapter_metadata.json
  • Check 6: Parity verification
  • Check 7: Oracle verification
  • Check 8: Link verification
  • Check 9: PR completeness
  • Check 10: Task generation verification
  • Check 11: Oracle smoke test
  • Check 12: Trust check

Previous Review — Fix Verification

All items flagged across prior reviews were checked. Status:

# Prior Issue Status
1 parity_pr must be array [...] in all 3 entries ✅ Fixed
2 README Usage: uv run lab canonical form ✅ Fixed
3 adapter_name should be "lab" ✅ Fixed
4 "None" strings should be JSON null ✅ Fixed
5 Entry 2 missing original/original_runs fields ✅ Fixed
6 dataset_pr org conflict (laude-institute vs harbor-framework) ✅ Fixed
7 xlsx harbor mean 0.7570.756 ✅ Fixed
8 Original xlsx SEM 0.0300.017 (std dev used instead of SEM) ✅ Fixed in parity_experiment.json, README, and adapter_metadata.json
9 Stale "in-flight" xlsx notes in adapter_metadata.json ✅ Fixed

All nine previously-flagged issues are resolved.


Check 1 — Adapter Code Layout & Logic ✅

  • src/lab/adapter.py, src/lab/main.py, src/lab/__init__.py at correct new-layout paths ✓
  • __init__.py contains only __all__ = []
  • main.py supports --output-dir, --limit, --overwrite, --task-ids, --split, --repo-cache-dir, --skip-clone
  • main.py imports LabAdapter from .adapter and calls adapter.run()
  • Class named LabAdapter (PascalCase + Adapter suffix) with run(self) method ✓
  • pyproject.toml name harbor-lab-adapter; scripts entry lab = "lab.main:main"
  • Default output path: HARBOR_ROOT / "datasets" / "lab"
  • pathlib.Path throughout; subprocess.run(..., check=True) propagates git errors; .read_text(encoding="utf-8") for file reads ✓
  • deliverables_map handles both dict and list forms of upstream deliverables
  • _apply_split correctly handles main, parity, xlsx, and practice-area filters ✓
  • TOML escaping: desc.replace('"""', '\\"\\"\\"') — correct ✓
  • No dead code or unused imports ✓
  • No solution/ folder — intentional and documented ✓

Check 2 — README.md ✅

  • All required sections present: Overview, What is, Adapter Features, Generated Task Structure, Run Evaluation, Usage, Parity, Notes & Caveats, Installation, Troubleshooting, Citation, Authors ✓
  • Task count (1,251), slice sizes (50/25), run counts (3) consistent with parity_experiment.json
  • "Usage: Create Task Directories" shows uv run lab --output-dir ../../datasets/lab ✓ (canonical form)
  • Reproduction section (README.md:167) uses uv run --project adapters/lab lab — this is uv run lab with a --project prefix, acceptable ✓
  • All parity numbers in README table match JSON ✓
  • Content reads naturally ✓

Check 3 — task-template/ Files ✅ (minor note)

  • task.toml has [task].name = "harveyai/{task_name}"
  • No canary strings ✓
  • No harbor-unrelated comments ✓
  • tests/test.sh invokes rewardkit /tests (writes to /logs/verifier/reward.txt) ✓
  • Timeouts: verifier.timeout_sec = 1800.0, agent.timeout_sec = 7200.0 — well-justified ✓
  • environment/Dockerfile uses digest-pinned lab-sandbox@sha256:cf4dac… with minimal COPY documents/ layer ✓
  • No solution/solve.sh — intentional and documented ✓

Minor note (carried from all prior reviews): task.toml has authors = [{ name = "Harvey AI" }] — the spec schema requires { name, email }. Harvey AI has no canonical contact email; email = "" as a placeholder would satisfy the schema. Not blocking.


Check 4 — parity_experiment.json ✅ (minor note)

All three entries reviewed. SEM calculations independently verified using sqrt(Σ(xᵢ−x̄)²/(n·(n−1))):

  • Entry 1 (v0 custom judge):
    • orig all_pass [0.04, 0.04, 0.06] → SEM = 0.0067 ≈ 0.007 ✓; harbor [0.08, 0.04, 0.06] → SEM = 0.01155 ≈ 0.012 ✓
    • orig per_crit [0.799, 0.816, 0.816] → SEM = 0.00567 ≈ 0.006 ✓
    • harbor per_crit [0.799, 0.746, 0.804] → SEM = 0.01856 ≈ 0.019 — reported as 0.018. Minor rounding discrepancy in the historical v0 record; not in the headline (entry 2) numbers.
  • Entry 2 (v1 rewardkit parity):
    • harbor all_pass [0.08, 0.0, 0.04] → SEM = 0.023 ✓; harbor per_crit [0.837, 0.82, 0.795] → SEM = 0.012 ✓
    • original/original_runs fields present, reusing entry 1 values with explanation in notes ✓
  • Entry 3 (xlsx):
    • harbor per_crit [0.764, 0.702, 0.803] → SEM = 0.029 ✓; orig per_crit [0.724, 0.75, 0.69] → SEM = 0.017 ✓ (correctly fixed from prior 0.030)
    • all_pass trivially 0.000 ± 0.000 on both sides ✓

parity_pr is array [...] in all 3 entries ✓. No data inconsistencies between README and JSON ✓.

Minor note: Entry 1 harbor per_crit SEM is 0.018 but computes to 0.019. This is a historical v0 record — the headline parity numbers are in entry 2 — so this is not blocking.


Check 5 — adapter_metadata.json ✅

  • "adapter_name": "lab"
  • adapter_builders: ["Ryan Marten ([email protected])"]
  • supported_agents: null, added_agents: null, parity_unmatching_agents: null (JSON null, not string "None") ✓
  • Sizes match parity_experiment.json (adapted=1251, parity=50, xlsx=25) ✓
  • xlsx entry notes reads "per-criterion 0.721 ± 0.017" ✓ (SEM correctly updated from prior 0.030)
  • Format matches template ✓

Check 6 — Parity Verification ✅

  • Both slices (parity-50, xlsx-25) have 3 runs per side ✓
  • Scores reported as mean ± sample SEM ✓
  • Agent version specified: [email protected]
  • Standard CLI agent (claude-code) used ✓
  • Range-overlap criterion verified for all metrics:
    • Parity per-criterion: max(orig)=0.816 ≥ min(harbor)=0.795; max(harbor)=0.837 ≥ min(orig)=0.799 ✓
    • Parity all-pass: max(orig)=0.06 ≥ min(harbor)=0.0; max(harbor)=0.08 ≥ min(orig)=0.04 ✓
    • xlsx per-criterion: max(orig)=0.750 ≥ min(harbor)=0.702; max(harbor)=0.803 ≥ min(orig)=0.690 ✓
    • xlsx all-pass: all 0.0 on both sides — trivially overlap ✓
  • Cross-judge bridging (95.34%/92.92% agreement, κ ≈ 0.82–0.85) adequately justifies comparing rewardkit Harbor side vs custom-judge original side ✓
  • Parity subset selection (first 50 alphabetically; first 25 xlsx tasks) clearly documented ✓

Check 7 — Oracle Verification ✅

No oracle solution provided — Harvey LAB ships no gold deliverables. The README explicitly documents this ("Harvey LAB ships no gold deliverables, and we do not generate any oracle solutions ourselves since it would require immense effort and expertise."). Generating valid legal work products across 1,251 tasks would require substantive domain expertise and could not be meaningfully verified by the same rubric judge used for evaluation. This is a genuine constraint, not a shortcut. ✓


Check 8 — Link Verification ⚠️

External URLs cannot be fetched in this environment. Static analysis:

  • adapter_pr: harbor-framework/harbor/pull/1591 — this PR ✓
  • dataset_pr: https://github.com/laude-institute/harbor-datasets/pull/229 — consistently laude-institute
  • parity_pr: https://huggingface.co/datasets/harborframes/parity-experiments/discussions/250harborframes namespace unverified. The adapter tutorial references harborframeworks/parity-experiments (with an 's'); the JSON uses harborframes (without 's'). This discrepancy has been noted since review 1 and cannot be verified without network access. Please confirm the URL is accessible.

Check 9 — PR Completeness ✅

  • adapter_pr: this PR (harbor-framework/harbor/pull/1591) ✓
  • dataset_pr: present and consistent (laude-institute) ✓
  • parity_pr: present ✓

Check 10 — Task Generation Verification ✅

  • tasks_dir.rglob("task.json") correctly walks upstream structure ✓
  • All template placeholders populated: {task_name}, {work_type}, {keywords}, {artifacts}, {instructions}
  • judge.toml dynamically generated per-task; """\"\"\" escaping correct ✓
  • Documents directory copied per-task; .keep placeholder for tasks without documents ✓
  • make_local_task_id ensures unique IDs via practice-area prefix ✓
  • deliverables_map handles both dict and list forms of upstream deliverables field ✓
  • _render_instruction rewrites deliverable filenames to /workspace/output/<file> paths and appends the documents pointer ✓

Check 11 — Oracle Smoke Test ✅

No solution/solve.sh — intentional and documented. tests/test.sh correctly invokes rewardkit /tests with JUDGE_CONCURRENCY plumbed through from [verifier.env] in task.toml. Dockerfile uses digest-pinned image with full document toolchain baked in. ✓


Check 12 — Trust Check ✅

Implementation is thorough and transparent. Parity methodology is comprehensively documented — including the v0→v1 judge migration, the A/B cross-judge equivalence study at format granularity (.docx and .xlsx separately), and the outlier analysis. The internet-access deviation from the original benchmark is explicitly called out in the README's task.toml section. No suspicious shortcuts or undocumented simplifications detected. ✓


Summary

All previously flagged "should fix" items are resolved. No new blocking issues found.

This adapter is ready for merge.

Remaining nice-to-haves (not blocking):

# File Note
1 task-template/task.toml Add email = "" to authors entry to satisfy { name, email } schema — carried from all prior reviews
2 parity_experiment.json (entry 1) Harbor per_crit SEM shown as 0.018; computes to 0.019 from runs [0.799, 0.746, 0.804]. Historical v0 record only — not the headline entry
3 All JSON files Verify parity_pr HuggingFace namespace (harborframes vs harborframeworks) — open since review 1

@smiky2011 smiky2011 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @RyanMarten, this adapter generally LGTM!👍

@Ternura143 Ternura143 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @RyanMarten , LGTM! Just a few minor issues, could you check them again?

Comment thread adapters/lab/README.md
Comment on lines +340 to +350
## Authors & Contributions

This adapter is developed and maintained by [Ryan Marten](mailto:[email protected])
from the Harbor team. Original benchmark by [Harvey AI](https://github.com/harveyai/harvey-labs).

**Issues and Contributions:**

- Submit Issues and Pull Requests to the main repository
- Follow the project's coding style and commit guidelines
- Please run `uv run python scripts/validate_adapter.py adapters/lab` before
submitting and include parity numbers if you run them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write as:

Authors & Contributions

This adapter is developed and maintained by Ryan Marten from the Harbor team.

Issues and Contributions:

  • Submit Issues and Pull Requests to the main repository
  • Follow the project's coding style and commit guidelines

Comment thread adapters/lab/README.md
Comment on lines +352 to +355
## Acknowledgement

Harvey LAB is released under the MIT License. See the
[original repo](https://github.com/harveyai/harvey-labs) for full license text.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need this section unless you are using the 2077 API. If this is the case, please write:

Acknowledgement

API inference compute for running parity tests is generously supported by 2077AI (https://www.2077ai.com/).

@elliotvaucher

Copy link
Copy Markdown

Hi @RyanMarten , LGTM! Just a few minor issues, could you check them again?

any news on this adapter ?

timeout_sec = 1800.0

[verifier.env]
ANTHROPIC_API_KEY = "${ANTHROPIC_API_KEY}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only supporting ANTHROPIC_API_KEY, a single provider for verification is an antipattern

real-estate/extract-psa-key-terms/scenario-01
-> real-estate-extract-psa-key-terms-scenario-01
"""
return source_id.lower().replace("_", "-").replace("/", "-")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replacement logic is incorrect, should be corporate-ma__review-data-room-red-flag-review (no slashes)

"""
lines: list[str] = [
"[judge]",
'judge = "anthropic/claude-sonnet-4-6"',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more hard coded models

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants