| name | harbor-index-release |
| description | Orchestrate a full Harbor Index release cut (build-push-pin, oracle gate, Hub publish, leaderboard cutover, image aliases, signed tag / GitHub Release) or a hotfix re-pin. Use when a maintainer asks to cut harbor-index vX.Y, ship a release, do a hotfix release, publish harbor-index, or run the release runbook. Does not cover post-release GPT evals or leaderboard submissions. |
| compatibility | Requires git, gh, uv, harbor CLI, Docker Hub login for alias; GitHub workflow_dispatch on this repo; Modal for oracle sweeps. |
| metadata | {"author":"harbor-framework","standard":"agentskills.io"} |
Harbor Index release
Drive the real multi-step release. There is no one-shot “trigger release” workflow.
| Automated | Manual / agent-orchestrated |
|---|
build-push-pin.yml (workflow_dispatch) → builds, pins, pushes release/X.Y | Oracle gate, Hub publish, leaderboard cutover |
github-release.yml (tag push harbor-index-*) → GitHub Release | Image aliases, GPG signed tag |
Canonical tooling detail (do not duplicate here):
OUT OF SCOPE: GPT evals, leaderboard submissions (lb submit), task-maintenance docs (VERIFIER_TIMEOUTS.md, ADAPTATIONS.md).
Progress checklist
Release Progress:
- [ ] 0. Confirm version + mode with user (STOP)
- [ ] 1. Preconditions (main green / FROM pins)
- [ ] 2. build-push-pin (dispatch; execute=true needs STOP)
- [ ] 3. Oracle 5× + oracle-gate attestation commit
- [ ] 4. Harbor publish (tasks → dataset) + commit dataset.toml (STOP)
- [ ] 5. Leaderboard create + cutover constants (STOP before merge)
- [ ] 6. Image aliases + signed tag + verify GitHub Release (STOP)
- [ ] 7. Report links / remaining follow-ups
STOP / ask-user gates
Halt and get explicit approval before:
- Version / mode — confirm
X.Y (initial) vs X.Y.Z (hotfix) and target branch.
execute=true on build-push-pin (builds/pushes ~171 images; dry-run is default).
- Public Hub publish (
harbor publish … --public).
- GPG / signed tag (
git tag -s) and pushing harbor-index-*.
- Merging PRs (leaderboard cutover to
main, etc.).
Never invent a fake end-to-end workflow. Prefer dry-run / preview first.
Auth needed
gh with workflow dispatch + contents permissions
- Docker Hub login for local
alias (CI uses DOCKERHUB_*)
harbor auth login / HARBOR_API_KEY for publish + leaderboard create
- Modal + judge keys for oracle (
job-config.yaml verifier envs)
Step 0 — Confirm version
- Initial: version
X.Y, branch release/X.Y, tag harbor-index-X.Y — must not already exist.
- Hotfix: version
X.Y.Z, dispatch from existing release/X.Y after cherry-picks from main.
Step 1 — Preconditions
git fetch origin --prune --tags
uv run scripts/build_push_pin.py lint --scope main
gh run list --branch main --limit 10
- Every
FROM must be digest-pinned on main; no docker_image pins on main.
- If FROMs are unpinned:
resolve-from-digests → PR to main before cutting.
- Use a clean worktree of the release ref for oracle/publish/tag work.
Step 2 — build-push-pin
Inputs: version (X.Y / X.Y.Z), mode (initial|hotfix), execute (false default).
Guards when execute=true:
- initial: from
main; release/X.Y and tag harbor-index-X.Y must not exist.
- hotfix: from
release/<minor>; version must be X.Y.Z.
Initial (from main):
gh workflow run build-push-pin.yml --ref main \
-f version=X.Y -f mode=initial -f execute=true
Hotfix (from release/X.Y, after cherry-picks):
gh workflow run build-push-pin.yml --ref release/X.Y \
-f version=X.Y.Z -f mode=hotfix -f execute=true
Monitor until success. Download digests-merged → keep as digests.json (needed for oracle-gate and alias).
Local bootstrap alternative: uv run scripts/build_push_pin.py release --version X.Y --execute
If a partial initial run already created release/X.Y, do not re-dispatch mode=initial — switch to hotfix.
Step 3 — Oracle gate
From a checkout of release/X.Y (pinned images):
- Full 5× oracle on required tasks (= all tasks minus
ORACLE_SKIP in scripts/build_push_pin.py), Modal amd64, upload public:
harbor run --job-name release/oracle-X.Y-… \
-c job-config.yaml -p tasks \
--include-task-name <each-required-task> … \
-a oracle -e modal -k 5 -n 32 \
--upload --public -y
- Attest and commit (STOP if gate fails):
uv run scripts/build_push_pin.py oracle-gate \
--version X.Y --job <hub-uuid> --digests-in digests.json --execute
git add release-oracle/X.Y.json
git commit -m "release: attest vX.Y oracle gate"
git push origin release/X.Y
--execute requires --job (hub UUID) and --digests-in. --trials-in is dry-run/offline only. Attestation should record all-1.0 reward lists; alias --execute later requires a hub-linked attestation whose digest_set_sha256 matches the digest map.
Step 4 — Harbor publish
STOP before --public. Order is mandatory: tasks, then dataset. No --yes on harbor publish — pipe confirmation. Pass tasks/* explicitly (nested dirs are not always auto-collected).
harbor init harbor-index/harbor-index --dataset --output-dir . \
--description "Harbor Index: 80 agentic … (revision X.Y)." \
--author "Harbor Framework"
harbor add tasks --scan --to dataset.toml
printf 'y\n' | harbor publish tasks/* --tag X.Y --public --concurrency 20
printf 'y\n' | harbor publish . --tag X.Y --public --concurrency 20
Commit dataset.toml on the release branch before tagging so github-release can attach it. Resolve published dataset content hash from Hub for DATASET_REF.
Step 5 — Leaderboard cutover
- Create hub leaderboard
harbor-index-X-Y (schema in leaderboard/SETUP.md):
harbor hub leaderboard create --config leaderboard.json --json
- Update on release branch and open a PR to
main (STOP before merge):
| File | Field |
|---|
leaderboard/src/leaderboard/core/hub.py | DATASET_REF = "sha256:…" |
leaderboard/src/leaderboard/ci/submit.py | LEADERBOARD_NAME = "harbor-index-X-Y" |
leaderboard/SETUP.md | Example name / title in leaderboard.json |
Root README.md | Leaderboard URL query leaderboard=harbor-index-X-Y |
leaderboard/src/leaderboard/display_names.json | Optional new display rows |
Step 6 — Aliases + signed tag + GitHub Release
uv run scripts/build_push_pin.py alias \
--version X.Y --digests-in digests.json --execute
git tag -s harbor-index-X.Y -m "harbor-index X.Y (frozen image set)"
git push origin harbor-index-X.Y
gh run list --workflow github-release.yml --limit 3
gh release view harbor-index-X.Y
github-release.yml uses --verify-tag (unsigned tags fail) and attaches release-oracle/X.Y.json + dataset.toml when present. Release notes need an explicit previous harbor-index-* tag (previous_tag_name) because divergent release branches are not linear ancestors (#64).
Hotfix path
- Land Dockerfile/source fix on
main.
- Cherry-pick onto
release/X.Y.
- Dispatch
build-push-pin with mode=hotfix, version X.Y.Z, --ref release/X.Y.
- Re-run oracle (at least touched required tasks; full gate before alias if attestation must rebind), re-publish if task digests changed, re-alias, tag
harbor-index-X.Y.Z if shipping a tagged hotfix.
Drift: after cherry-pick, tree hashes intentionally diverge from old pins. Preflight may use lint --scope release --allow-drift when that flag exists; aggregate still requires drift-free lint --scope release after re-pin. If preflight fails on drift and --allow-drift is missing on the ref, stop and fix tooling before re-dispatching.
Pitfalls
- Divergent release branches → release-note baseline needs previous tag (#64).
alias without --digests-in → --execute refuses.
- Publish order → tasks before dataset.
- Evaluate frozen digests /
@X.Y, not floating main.
- Unsigned tags →
--verify-tag fails.
- Hotfix drift / Modal CPU variance can flake oracles (e.g. fastText
-march=native).
- Dirty worktrees → use a clean release worktree.
- Missing GPG → stop and ask; do not silently fall back without approval.
Done when
release/X.Y has pins + release-oracle/X.Y.json + dataset.toml
- Hub has public
harbor-index/harbor-index@X.Y
- Leaderboard
harbor-index-X-Y exists; cutover PR ready/merged on main
- Alias tags stamped; GitHub Release published for
harbor-index-X.Y