Skip to content

Add first-class Harbor integration for agent-eval campaigns#5029

Open
htahir1 wants to merge 23 commits into
developfrom
feature/add-harbor-integration
Open

Add first-class Harbor integration for agent-eval campaigns#5029
htahir1 wants to merge 23 commits into
developfrom
feature/add-harbor-integration

Conversation

@htahir1

@htahir1 htahir1 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Describe changes

Promotes the Harbor example into a first-class zenml.integrations.harbor integration for running Harbor agent-eval campaigns as ZenML pipelines. ZenML owns the outer orchestration (matrix expansion, per-shard steps, retries, caching, artifacts, reports); Harbor keeps the trial eval kernel (task loading, agent loop, verifier, reward). Trials execute inside the active stack's Sandbox component via the ZenMLSandboxEnvironment bridge (relocated from the example, behavior-identical).

Integration (src/zenml/integrations/harbor/):

  • models.py / utils.py (harbor-free): typed campaign contracts, stable sha256 trial identities (task + agent + model + kwargs + env + index — the cross-run baseline join key), deterministic single-cell shard packing with a trials_per_step knob.
  • job_runner.py: one single-cell Harbor JobConfig per shard, run via await Job.create(...) / job.run(), per-trial results from the in-memory JobResult.trial_results. Dataset source is deliberately kept off the Harbor-side config (fixes an IndexError in Harbor 0.8's quiet-mode display for client-side-resolved registry datasets) and restored onto results afterward.
  • materializers/: HarborShardResultMaterializer persists the flat summary plus Harbor's full job tree (agent/verifier logs, trajectories) as a tar archive; queryable metadata, markdown visualization, on-demand download_jobs_dir(). Pinned on the step via output_materializers so transitive-dependency drift can't silently degrade to the pydantic fallback.
  • steps/: build_harbor_matrixrun_harbor_shard (mapped; logs harbor.* step metadata; optional fail_on_trial_error rescues the shard result + log archive as harbor_shard_result_<id>_failed before failing) → build_harbor_report.

Example (examples/harbor_agent_evals/, renamed from sandbox_harbor): default is a hermetic oracle-vs-nop head-to-head (no keys) that prints its own receipts — report, metadata scan, restored log tree; dataset:NAME@VERSION[:N] CLI slicing for registry benchmarks.

Docs: new top-level Agent evals guide (user-guide/agent-evals-guide/): overview, Run an eval campaign, Benchmark operations (delta reruns, error gating, cross-run identity joins, regression gates), From evals to training (SFT from winning trajectories, rejection sampling, config sweeps, eval-gated promotion). Plus the materializers table row and a sandboxes-page cross-reference. Cross-space references use absolute docs.zenml.io URLs.

Validation (all live, on Modal sandboxes)

  • Local campaigns: oracle 1.000 vs nop 0.000, 6 trials / 4 shards; identical rerun fully cache-hits (47s → 19s).
  • Resume-after-failure: a genuine Modal session hang on one shard; rerun cache-hit the finished shards and re-executed only the hung one.
  • Registry benchmarks: terminal-bench-sample@2.0 — chess-best-move, polyglot-c-py, sqlite-with-gcov, all reward 1.000 under oracle, incl. per-task docker_image overrides.
  • Batching: one shard ran 4 trials concurrently in 21s (single trial ≈ 15s).
  • fail_on_trial_error: live rescue artifact verified (archive restorable, exception.txt + job.log present).
  • Real LLM agent: claude-code + claude-sonnet-4-5 on chess-best-move — clean run, reward 0.000 (a genuine benchmark result), $0.1193 tracked through trial → metadata → report.
  • 66 unit tests (incl. a Harbor 0.8 API canary and an import-safety canary), mypy strict clean, verified on a harbor-less Python 3.11 env (gated tests skip via harbor.job — namespace-package-proof).

Areas needing reviewer attention

  • Python gating: Harbor requires >= 3.12; get_requirements() returns [] below (quiet on the interpreter fallback), check_installation() is version-guarded.
  • CI installs: harbor is skipped on 3.10/3.11 legs and installed on 3.12+ (pulls litellm/datasets/supabase) so the gated tests run — flag if you'd rather confine to one leg.
  • Bridge uses two Harbor protected members (_merge_env, _validate_definition override) — Harbor's intended subclass hooks.
  • Test layout: tests/unit/integrations/harbor/ deliberately has NO __init__.py (would shadow the real harbor package); see the conftest guard.
  • mypy: harbor ships no py.typedignore_missing_imports + disallow_subclassing_any=false scoped to the environment module.
  • Post-release docs sweep: deepen /stacks links to /stacks/sandboxes and link the guide from the sandboxes page once published.

Non-goals / extension points

No Harbor Hub upload, no built-in baseline store, no multi-step tasks or separate verifier sandboxes (rejected with NotImplementedError), no Sandbox core changes. Known bridge limitations (no resource translation, no exec(user=...), allow_internet=false refused, docker_image override Modal-only) preserved and documented. Trajectory→training-dataset utilities are the natural follow-up (the archives already carry rollouts + rewards; see the From evals to training guide).

Related: #5030 (remote image builders re-apply .dockerignore and drop generated context files), found while validating the Modal-orchestrator path.

Pre-requisites

  • I have read the CONTRIBUTING.md document.
  • I have added tests to cover my changes.
  • I have based my new branch on develop and the open PR is targeting develop.
  • IMPORTANT: My changes are reflected properly in the following resources:
    • ZenML Docs (Agent evals guide, materializers table, sandboxes cross-reference)
    • Dashboard: Needs to be communicated to the frontend team.
    • Templates: Might need adjustments.
    • Projects

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Other (add details above)

Promote the sandbox_harbor example into zenml.integrations.harbor:
typed campaign models with stable trial identities, deterministic
single-cell shard packing (trials_per_step knob), a shard-result
materializer that archives Harbor's job tree with queryable metadata
and a markdown visualization, reusable build/run/report steps, and the
ZenMLSandboxEnvironment bridge relocated from the example. The example
becomes a thin consumer of the integration.

Sharding maps each mapped step onto one single-cell Harbor job, so
step-level caching reruns only failed or new shards of a campaign and
a failure invalidates the narrowest possible slice.

Harbor stays optional everywhere: registry-facing modules never import
it, requirements are gated to Python >= 3.12, and the shard step pins
its materializer explicitly so a transitive-dependency version drift
cannot silently degrade artifacts to the generic pydantic fallback.

Verified end-to-end on Modal sandboxes (local and remote tracking
servers), plus 64 unit tests including a Harbor 0.8 API canary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added internal To filter out internal PRs and issues enhancement New feature or request labels Jul 4, 2026
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Documentation Link Check Results

Absolute links check passed
Relative links check passed
Last checked: 2026-07-17 10:02:21 UTC

htahir1 and others added 2 commits July 3, 2026 21:12
Rename examples/sandbox_harbor to examples/harbor_agent_evals and turn
the default invocation into a hermetic head-to-head: oracle (reference
solution, reward 1.0) vs nop (does nothing, reward 0.0), 3 trials per
cell packed 2 per shard — so one command demonstrates matrix expansion,
shard packing, and a report with contrasting rows, still with no LLM
keys required.

After the run the example prints its own receipts: the campaign report,
the losing shards found via the logged harbor.* step metadata, and one
shard's Harbor job tree restored from the artifact store. Rerunning the
same command demonstrates per-shard caching (completed shards cache-hit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On CI legs without Harbor installed, pytest.importorskip("harbor")
passed anyway: with no __init__.py, the test directory itself satisfies
a bare `import harbor` as a namespace package (real distributions win
when installed, which is why this never reproduced locally), and the
gated modules then crashed on `harbor.job`. Skip on a concrete
submodule a namespace package can never provide.

Also point the materializer table at the GitHub source instead of the
sdkdocs page that only exists after the next release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1 htahir1 added release-notes Release notes will be attached and used publicly for this PR. run-slow-ci Tag that is used to trigger the slow-ci and removed run-slow-ci Tag that is used to trigger the slow-ci labels Jul 4, 2026
Bandit (B108) flags hardcoded temp paths at medium severity, failing
the security check. The strings were inert fixture placeholders, so
plain non-tmp values work just as well.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1 htahir1 added run-slow-ci Tag that is used to trigger the slow-ci and removed run-slow-ci Tag that is used to trigger the slow-ci labels Jul 5, 2026
htahir1 and others added 2 commits July 5, 2026 08:55
Shards resolve datasets client-side, so the Harbor job only registers
metrics under its adhoc bucket. Forwarding the dataset name as the
task's `source` made Harbor 0.8's quiet-mode progress hook look up the
missing dataset bucket and crash with an IndexError on every scored
registry trial. Keep the provenance on the shard spec instead and
restore it onto trial results after the run.

Verified against the live registry: terminal-bench-sample@2.0, 3 tasks
under the oracle agent on Modal, all rewards 1.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Allow dataset:NAME@VERSION:N on the CLI to slice a registry dataset to
its first N tasks, so a first benchmark run does not fan out the full
task set. Replace the hypothetical registry section in the README with
the verified terminal-bench-sample run and its actual report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1
htahir1 force-pushed the feature/add-harbor-integration branch from 15b0144 to a8ff442 Compare July 5, 2026 16:08
htahir1 and others added 6 commits July 5, 2026 11:28
Both learned by running the branch against live benchmarks: provider
keys reach the sandbox from the host environment (with the model on
the agent spec, and cost/tokens flowing into results, metadata and the
report), and fail_on_trial_error rescues the shard result plus log
archive as a named manual artifact before failing the step — while
without the flag an errored shard completes and gets cached.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A dedicated page under the Sandboxes component guide covering the
campaign pipeline shape, shard semantics (retry/cache unit, the
errored-shard caching caveat and its rescue artifact), artifacts and
queryable metadata, real-LLM-agent key handling, and the environment
bridge with its documented limitations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The component guide is for stack component flavors; evaluating agents
with Harbor is a user workflow and sits naturally alongside the other
standalone agents-guide pages. Keep a cross-link from the Sandboxes
component page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Positions the improvement loop Harbor unlocks on top of existing ZenML
primitives: regression gates joined by stable trial identities, SFT
dataset construction from reward-filtered trajectories in the archived
job trees, rejection-sampling loops using trials_per_cell as a scored
rollout generator, config/prompt sweeps keyed by identity-distinct
agent configurations, and eval-gated promotion via the Model Control
Plane. Every stage is a pipeline consuming campaign artifacts — no new
framework surface required.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The evals-and-training story is a workflow of its own, not an appendix
to the agents-guide tour: user-guide/agent-evals-guide/ now carries an
overview (why campaigns are an operations problem), Run an eval
campaign, Benchmark operations (delta reruns, error gating, cross-run
joins, regression gates), and From evals to training. The agents guide
returns to its original chapters; the sandboxes component page links
to the new guide.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The user guide, component guide and concepts docs publish as separate
GitBook spaces, so relative links between them break on the published
site. Cross-space references now use docs.zenml.io URLs that resolve
today; the sandboxes page names the not-yet-published guide without a
link to keep the absolute-link check green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
strickvl added a commit that referenced this pull request Jul 9, 2026
B1 ran on branch spike/b1-harbor-k8s (off open PR #5029, pushed,
deliberately unmerged — the docs are the deliverable). Hermetic Harbor
campaigns pass on the K8s sandbox flavor untouched; the docker_image
wall was proven hollow (KubernetesSandboxSettings.image already exists,
spike patch boots real Terminal-Bench images on EKS at oracle 1.000).
FINDINGS.md Theme 5 extended with the second ecosystem test; wrap-vs-
plugin assessment for Hamza lives on the spike branch.
@strickvl

strickvl commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

cc @htahir1

Ran this integration on the Kubernetes sandbox flavor (staging EKS) as part of the RL-spike follow-ups — validation so far was Modal-only, so here's the second-flavor datapoint. Full writeup: B1_K8S_FINDINGS.md on spike/b1-harbor-k8s (forked off this branch; spike docs only, not meant to merge here).

The good news: everything behind the sandbox abstraction reproduces untouched. Hermetic oracle-vs-nop: 6/6 trials, oracle 1.000 / nop 0.000, ~30s wall clock; identical rerun cache-hits all shards in 8s with zero pods; metadata, report, and download_jobs_dir() all work.

The docker_image wall is already hollow: all three terminal-bench-sample@2.0 tasks hit the designed NotImplementedError — but KubernetesSandboxSettings.image exists, so the condition in _settings_override's docstring ("switch to the active flavor's settings class when another flavor ships an image field") is met. A five-line patch on the spike branch (commit) translating docker_imageKubernetesSandboxSettings(image=...) boots all three real Terminal-Bench images on EKS at oracle reward 1.000. Fine as a fast-follow rather than in this PR — open questions are private-registry credentials, image arch, and node disk pressure at 89-task fan-out.

Two small review-relevant observations from the failure path:

  1. When start() raises, Harbor's cleanup calls exec() on the never-started env, so the user sees RuntimeError: ZenMLSandboxEnvironment used before start() + "Failed to download logs" noise above the real cause. Worth carrying the original failure in that error, or no-op'ing log download when no session was created.
  2. A campaign where every trial errors reports each trial as Completed=1 and Errored=1, mean reward n/a — and a harbor.mean_reward < 1 metadata query returns zero shards (no metadata logged for errored shards). Anyone wiring a regression gate to mean reward would pass an all-error campaign; harbor.n_errored:gt:0 is the query that works. Maybe worth a sentinel reward or an errors-first example receipt.

strickvl added a commit that referenced this pull request Jul 9, 2026
The Harbor->RL trajectory exporter (branch spike/b3-exporter, merges
nowhere) answered the eval->training lineage question: expressible and
demonstrated on B1's staging campaigns, but List[ArtifactVersionResponse]
step inputs and .map() over historical artifacts are both unsupported,
the #5029 trial_identity fragments across task-specification routes,
and no layer records the resolved sandbox image.
@strickvl

strickvl commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Follow-up from the same RL-spike track (see K8s comment above): I built the "From evals to training" story this PR's docs promise — a trajectory exporter that turns campaign shard artifacts into an accumulating RL-episode-candidates dataset artifact, run against real campaigns. Full write-up: B3_EXPORTER.md on spike/b3-exporter. The good news: the lineage edge works end-to-end, and the sha256 trial identity is exactly the right join key. Three findings are directly relevant to this PR, one fixable before merge:

  1. trial_identity fragments across task-specification routes. It hashes the full TaskRef dump, and the dataset resolver stamps source: into the ref while a direct git+URL@COMMIT:SUBPATH ref doesn't — so two campaigns attempting the byte-identical pinned task get different identities (hit this live). Local-path tasks hash their absolute path, making identities machine-specific. Suggestion: hash only the canonical pin coordinates (effectively TaskRef.to_string()), which is a small change in utils.trial_identity and cheapest to do before merge, since shipped identities become baselines people join against.
  2. Nothing records the resolved sandbox image. The job archive carries neither flavor nor image, and reconstructing it from the run's stack config gives provably wrong answers for docker_image-pinned tasks (my Terminal-Bench records claimed python:3.11-slim). The bridge is the only party that knows — one field in the shard/trial result, same neighborhood as the docker_image→K8s translation from the previous comment.
  3. task_checksum is a one-line lift. Harbor writes it into every trial result.json but HarborTrialResult.from_harbor doesn't surface it — it's a content-addressed complement to the coordinate-based identity (and would fix the local-path half of finding 1).

Addresses strickvl's review findings on the Harbor integration
(PR #5029), from the K8s and trajectory-exporter spikes:

- trial_identity now hashes the task's canonical pin coordinates
  (TaskRef.to_string()) instead of the full ref dump, so dataset-resolved
  and direct git+ refs to the byte-identical pinned task get the same
  identity. Shipped identities become baselines people join against, so
  this lands before merge.
- Surface Harbor's task_checksum on HarborTrialResult as the
  content-addressed complement to the coordinate-based identity.
- Record sandbox provenance (flavor + resolved docker_image override)
  per trial. The environment bridge is the only party that knows which
  image actually backed a trial; reconstructing it from stack config is
  provably wrong for docker_image-pinned tasks.
- Carry the original start() failure into the errors Harbor's cleanup
  path triggers on a never-started environment, so the root cause is no
  longer buried under "used before start()" noise.
- Stop double-counting errored trials: Harbor's n_completed includes
  errored trials, so reports and metadata now use n_succeeded
  (harbor.n_succeeded), and an all-errored shard logs
  harbor.mean_reward=0.0 so reward-threshold regression gates fail safe.

The docker_image -> KubernetesSandboxSettings translation from the same
review is deliberately left as a fast-follow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1

htahir1 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Both landed in 7d3a3bd: trial_identity now hashes the canonical pin (TaskRef.to_string()), task_checksum is on the trial result, and the bridge records sandbox_flavor/sandbox_docker_image per trial (session id == trial name made the join trivial).

Also from your K8s comment: start() failures now carry through to the cleanup-path errors, and the Completed/Errored double-count is gone — reports use n_succeeded, and an all-errored shard logs harbor.mean_reward = 0.0 so the documented reward gate fails safe.

The docker_imageKubernetesSandboxSettings translation stays a fast-follow per your note.

htahir1 and others added 2 commits July 9, 2026 11:36
Fixes the confirmed findings from an adversarial review of the
previous commit:

- TaskRef.to_string() returned "" for registry package tasks (name/ref
  only, no path or git URL), collapsing every such task into one
  trial_identity and shard_id. Package refs now render as NAME@REF, and
  trial_identity refuses refs with no coordinates at all.
- The harbor.mean_reward=0.0 sentinel was gated on the wrong condition:
  it fired on healthy-but-unscored shards (Harbor's verifier rewards
  are optional) and was bypassed by partially-errored shards whose
  survivor-only mean passed reward gates. It now fires only when every
  trial errored, and every shard logs harbor.error_rate so gates can
  pair reward thresholds with an error check. Docs updated to match.
- A failed start() now discards this session's provenance entry, so a
  retried trial that never opened a sandbox can't inherit an earlier
  attempt's image; the shard runner clears the whole registry after
  stamping, so verifier-session entries no longer accumulate.
- start() cancellation (Harbor's build timeout, a BaseException) is now
  recorded as the root cause, and a successful restart clears the stale
  failure note.
- The materializer's artifact metadata and visualization now use
  n_succeeded, matching the report and step metadata.
- The job-runner test seeds provenance through the public functions
  instead of the private registry dict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cleanup pass over the two review-fix commits (no behavior change
beyond one additive artifact-metadata key):

- Replace the pop-per-trial + clear-afterwards provenance API with one
  atomic drain_session_provenance(): the shard runner drains right
  after job.run(), so the registry is reset even if result processing
  raises (the old ordering leaked entries when from_harbor raised).
- Promote error_rate to a HarborShardResult property next to
  n_succeeded/mean_reward and reuse it in step metadata; the
  materializer now emits it too, keeping artifact metadata in step
  parity.
- Collapse the twin start() except arms into one, store the exception
  object instead of a flattened string, and chain it into the
  _live_session RuntimeError (raise ... from) so Harbor's cleanup noise
  carries the full cause, not just a summary line.
- Deduplicate the n_succeeded/registry/sentinel rationale comments to
  their one canonical home each.
- Test hygiene: shared _patch_active_sandbox/_modal_sandbox helpers and
  an autouse registry-drain fixture in the environment tests; the steps
  fixture now errors trials from the end so the partial-error test uses
  it directly instead of hand-rebuilding trials.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1
htahir1 requested a review from strickvl July 9, 2026 18:54
htahir1 and others added 2 commits July 9, 2026 12:04
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pydoclint (DOC503) correctly flagged that the docstrings drifted when
the start-failure bookkeeping was restructured: start() now re-raises
CancelledError/Exception from its except arm, and the log-dir teardown
re-raise lives in _start().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1 htahir1 added the run-slow-ci Tag that is used to trigger the slow-ci label Jul 10, 2026
htahir1 and others added 4 commits July 10, 2026 09:47
harbor 0.8 requires datasets>=4.4.1 while the huggingface integration
pins datasets>=2.16.0,<4.0.0, so installing every integration into one
environment has no resolution — the >=3.12 lint/test jobs failed at
Setup environment. Skip harbor there like tensorflow/deepchecks; its
unit tests are importorskip-gated and keep running in environments that
have Harbor installed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses the confirmed findings of a follow-up adversarial review:

- The session-provenance registry moves from a module global to a
  ContextVar scope (session_provenance_scope). Dynamic mapped shard
  steps run concurrently as threads in one process (DagRunner's startup
  executor), so a global registry let one shard drain or reset another
  shard's entries; the scope rides the ambient context into Harbor's
  asyncio tasks and isolates per step thread.
- A trial cancelled mid-create_session (Harbor's environment build
  timeout) abandoned the worker thread and leaked whatever sandbox it
  eventually created. Creation now runs on a dedicated executor whose
  concurrent future gets a reap callback on cancellation — the session
  is destroyed in the worker thread the moment it materializes. (The
  asyncio run_in_executor wrapper can't do this: it reports itself
  cancelled while the worker keeps running.)
- Restore Harbor-backed CI coverage lost when harbor was excluded from
  the omnibus environment: a dedicated harbor-unit-test job in ci-fast
  installs zenml without integrations plus harbor on Python 3.12 and
  runs the gated tests, including the Harbor API canary.
- TaskRef.parse now rejects git pins that are not commit SHAs (branches,
  tags, empty pins) — mutable pins move underneath shard caching and
  trial-identity baselines. Docs updated to match.
- Sentinel docs/docstrings tightened: the 0.0 mean-reward sentinel
  applies to all-errored shards with no scored trials; errored-but-
  scored trials keep their real mean and are caught by the error-rate
  gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1

htahir1 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

@strickvl Can you take a look?

htahir1 and others added 2 commits July 17, 2026 11:33
Follow-up fixes for the PR #5029 review; each item targets a specific
concern raised there.

W1 (agent_env secrets persisted in cleartext): agent_env is serialized
verbatim into the harbor_shards artifact and every shard result, so a
raw provider key placed there leaks into the artifact store. Document
this explicitly in the campaign docs (use the ambient sandbox env or
ZenML secrets, never raw values) and log a warning from
build_harbor_matrix when an agent_env key looks credential-shaped. The
value is never scrubbed — silent mutation would be worse than a loud
warning, since dropping a var the agent needs fails opaquely.

W2 (temp job dir leak on long-lived processes): the materializer now
prunes the local zenml-harbor- temp job tree once it has archived it
into the artifact store (the durable copy). Deletion is bounded to a
direct child of the system temp dir carrying the zenml-harbor- prefix,
so a user-supplied job_dir is never removed. This must live in the
materializer, not the step's finally: output materialization runs after
the step function returns.

W3 (global debug verbosity in CI): no change needed. This branch already
scopes ZENML_DEBUG to the harbor-unit-test job's own env block and never
added ZENML_LOGGING_VERBOSITY=debug to install-zenml-dev.sh.

W4 (trial to identity pairing by order): expand the comment to state
that shard trials are exchangeable replicas of one (task, agent, model)
cell and the index is not a stable per-attempt identity. When Harbor
returns fewer results than identities, the missing trials are now
recorded as errored (counted in both n_completed and n_errored, matching
Harbor's convention that an errored trial is a finished one) so a
fail_on_trial_error gate trips on them instead of letting them vanish.

Also strengthen the upstream-API canary to assert the protected members
the bridge relies on (_merge_env, _validate_definition) and document the
harbor/huggingface co-resolution incompatibility (datasets>=4.4.1 vs <4)
in the integration docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request internal To filter out internal PRs and issues release-notes Release notes will be attached and used publicly for this PR. run-slow-ci Tag that is used to trigger the slow-ci

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants