Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes are recorded here. Benchwork remains a prerelease and uses
versioned schemas plus explicit migration notes for compatibility.

## 0.3.0rc3 — Repository release verification

- Fixed the release installer to retain the canonical wheel filename required
by `uv` and `pipx` during install, repair, and rollback.
- Split repository release verification from PyPI publication: GitHub Pages and
immutable-installer smoke tests now run without a package-index publish.

See [release notes](docs/en/releases/0.3.0rc3.md).

## 0.3.0rc2 — The Invitation

- Fixed `benchwork_open_task` minting every Task Capsule for the `codex` Host.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ tools may propose work, but only the deterministic Athanor kernel can accept a
canonical transition. Accepted events are written to a local, append-only
Chronicle with chained SHA-256 Sigils and receipts.

> Project status: `0.3.0rc2` release candidate. Phase 2 freezes the Codex
> Project status: `0.3.0rc3` release candidate. Phase 2 freezes the Codex
> Plugin-first Instrumentarium while Phase 3 adds a bounded contract-only,
> fail-closed local slice; canonical authority and RFC acceptance remain
> external prerequisites.
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Benchwork 将科研活动转化为明确、可审查的状态。智能体和工
提案,但只有确定性的 Athanor 内核能够接受规范状态迁移。获准事件写入本地
追加式 Chronicle,并通过链式 SHA-256 Sigil 与 Receipt 留下记录。

> 项目状态:`0.3.0rc2` 候选版本。Phase 2 已冻结 Codex Plugin-first
> 项目状态:`0.3.0rc3` 候选版本。Phase 2 已冻结 Codex Plugin-first
> Instrumentarium;Phase 3 增加了范围受限、默认 fail-closed 的本地合同纵切,
> 规范权威与 RFC 接受仍是外部前置条件。

Expand Down
22 changes: 22 additions & 0 deletions docs/en/releases/0.3.0rc3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
language: en
canonical: true
---

# Benchwork 0.3.0rc3 — Repository release verification

`0.3.0rc3` is a GitHub-repository-only release candidate. PyPI publication is
deliberately disabled for this release path.

## Fixed

- The installer now keeps the canonical distribution wheel filename when using
`uv` or `pipx`, including repair and rollback downloads.
- The release workflow can build, publish the GitHub Pages installer, and run
its immutable-install smoke test without publishing a Python package index.

## Scope

This candidate retains the Phase 3 contract-only boundary described in the
[0.3.0rc2 release notes](0.3.0rc2.md). It does not add external acceptance,
Chronicle, Storage, Athanor, or package-index authority.
17 changes: 14 additions & 3 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,16 @@ download() {
fail 4 "download exceeded ${limit} bytes: ${url}"
}

wheel_filename() {
wheel_url_value=$1
wheel_filename_value=${wheel_url_value%%\?*}
wheel_filename_value=${wheel_filename_value##*/}
case "${wheel_filename_value}" in
benchwork_arcana-*.whl) printf '%s\n' "${wheel_filename_value}" ;;
*) fail 4 "release manifest wheel URL has an unsupported filename" ;;
esac
}

sha256() {
if [ "${sha_command}" = sha256sum ]; then
sha256sum "$1" | awk '{print $1}'
Expand Down Expand Up @@ -596,7 +606,7 @@ if [ "${uninstall}" -eq 1 ] || { [ "${repair}" -eq 1 ] && [ "${version_set}" -eq
recovery_wheel_url=$(json_get "${recovery_manifest}" package.wheel.url)
recovery_wheel_sha=$(json_get "${recovery_manifest}" package.wheel.sha256)
recovery_wheel_size=$(json_get "${recovery_manifest}" package.wheel.size)
recovery_wheel=${tmp_root}/recovery.whl
recovery_wheel=${tmp_root}/$(wheel_filename "${recovery_wheel_url}")
download "${recovery_wheel_url}" "${recovery_wheel}" "${recovery_wheel_size}"
verify_sha "${recovery_wheel}" "${recovery_wheel_sha}"
PATH="${recovery_bin_dir}:${PATH}"
Expand Down Expand Up @@ -725,6 +735,7 @@ expected_plugin_version=$(printf '%s' "${version}" |
fail 4 "plugin version does not match the package version"
[ "${python_requirement}" = ">=3.11" ] || fail 4 "unsupported Python requirement"
[ "${wheel_size}" -le "${MAX_WHEEL_SIZE}" ] || fail 4 "wheel exceeds size limit"
wheel_filename "${wheel_url}" >/dev/null
case "${manifest_channel}" in
rc) case "${version}" in *rc*) ;; *) fail 4 "RC manifest has a non-RC version" ;; esac ;;
nightly) case "${version}" in *dev*) ;; *) fail 4 "nightly manifest has a non-nightly version" ;; esac ;;
Expand Down Expand Up @@ -986,7 +997,7 @@ fi
printf '%s\n' "$$" >"${lock_dir}/pid"

backend_bootstrapped=0
wheel_path=${tmp_root}/benchwork.whl
wheel_path=${tmp_root}/$(wheel_filename "${wheel_url}")
if [ "${reuse_cli}" -eq 0 ]; then
download "${wheel_url}" "${wheel_path}" "${wheel_size}"
verify_sha "${wheel_path}" "${wheel_sha}"
Expand Down Expand Up @@ -1021,7 +1032,7 @@ rollback_previous_installation() {
old_wheel_url=$(json_get "${old_manifest}" package.wheel.url)
old_wheel_sha=$(json_get "${old_manifest}" package.wheel.sha256)
old_wheel_size=$(json_get "${old_manifest}" package.wheel.size)
old_wheel=${tmp_root}/rollback.whl
old_wheel=${tmp_root}/$(wheel_filename "${old_wheel_url}")
download "${old_wheel_url}" "${old_wheel}" "${old_wheel_size}"
verify_sha "${old_wheel}" "${old_wheel_sha}"
if [ "${previous_backend}" = uv ] && command -v uv >/dev/null 2>&1; then
Expand Down
2 changes: 1 addition & 1 deletion plugins/benchwork/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "benchwork",
"version": "0.3.0-rc.2",
"version": "0.3.0-rc.3",
"description": "Auditable research workflows and scientific control-plane tools for Codex",
"author": {
"name": "Endofthestars",
Expand Down
2 changes: 1 addition & 1 deletion plugins/benchwork/benchwork-plugin-api.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schema_version": "benchwork-plugin-api/1.0",
"plugin": "benchwork",
"plugin_version": "0.3.0-rc.2",
"plugin_version": "0.3.0-rc.3",
"benchwork_api": "0.3",
"minimum_runtime": "0.3.0rc2",
"skills_api": "1",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "benchwork-arcana"
version = "0.3.0rc2"
version = "0.3.0rc3"
description = "An auditable workbench for agent-assisted computational research."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion src/benchwork/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Benchwork's deterministic Athanor foundation."""

__version__ = "0.3.0rc2"
__version__ = "0.3.0rc3"
4 changes: 2 additions & 2 deletions src/benchwork/install/compatibility.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"schema_version": "benchwork-install-compatibility/1.0",
"package": "benchwork-arcana",
"version": "0.3.0rc2",
"plugin_version": "0.3.0-rc.2",
"version": "0.3.0rc3",
"plugin_version": "0.3.0-rc.3",
"plugin_api": "benchwork-plugin-api/1.0",
"mcp_tool_registry": "mcp-tool-registry/1.0"
}
7 changes: 4 additions & 3 deletions tests/installer/test_installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@


ROOT = Path(__file__).parents[2]
VERSION = "0.3.0rc2"
PLUGIN_VERSION = "0.3.0-rc.2"
VERSION = "0.3.0rc3"
WHEEL_NAME = f"benchwork_arcana-{VERSION}-py3-none-any.whl"
PLUGIN_VERSION = "0.3.0-rc.3"


def artifact(name: str, *, size: int = 1) -> dict:
Expand All @@ -51,7 +52,7 @@ def manifest() -> dict:
"package": {
"name": "benchwork-arcana",
"requirement": f"benchwork-arcana=={VERSION}",
"wheel": artifact("benchwork.whl"),
"wheel": artifact(WHEEL_NAME),
"sdist": artifact("benchwork.tar.gz"),
},
"plugin": {
Expand Down
26 changes: 17 additions & 9 deletions tests/installer/test_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@


ROOT = Path(__file__).parents[2]
VERSION = "0.3.0rc3"


def run_exact_dry(document: str) -> subprocess.CompletedProcess[str]:
Expand Down Expand Up @@ -49,7 +50,7 @@ def run_exact_dry(document: str) -> subprocess.CompletedProcess[str]:
"--dry-run",
"--json",
"--version",
"0.3.0rc2",
VERSION,
],
cwd=work,
env=environment,
Expand Down Expand Up @@ -119,7 +120,7 @@ def test_exact_dry_run_is_non_mutating(self) -> None:
"--dry-run",
"--json",
"--version",
"0.3.0rc2",
VERSION,
],
cwd=work,
env=environment,
Expand All @@ -145,6 +146,12 @@ def test_manifest_mismatches_and_duplicate_keys_fail_before_mutation(self) -> No
self.assertEqual(result.returncode, 4)
self.assertIn("plugin version does not match", result.stderr)

invalid = manifest()
invalid["package"]["wheel"]["url"] = "https://example.test/benchwork.whl"
result = run_exact_dry(json.dumps(invalid))
self.assertEqual(result.returncode, 4)
self.assertIn("wheel URL has an unsupported filename", result.stderr)

result = run_exact_dry(
json.dumps(manifest()).replace(
'"schema_version": "benchwork-release-manifest/1.0"',
Expand All @@ -169,8 +176,8 @@ def test_rc_channel_verifies_its_manifest_descriptor(self) -> None:
channel = {
"schema_version": "benchwork-release-channel/1.0",
"channel": "rc",
"version": "0.3.0rc2",
"manifest_url": "https://example.test/releases/0.3.0rc2/release-manifest.json",
"version": VERSION,
"manifest_url": f"https://example.test/releases/{VERSION}/release-manifest.json",
"manifest_sha256": hashlib.sha256(manifest_blob).hexdigest(),
"manifest_size": len(manifest_blob),
}
Expand Down Expand Up @@ -210,7 +217,7 @@ def test_rc_channel_verifies_its_manifest_descriptor(self) -> None:
)
self.assertEqual(result.returncode, 0, result.stderr)
plan = json.loads(result.stdout)
self.assertEqual(plan["version"], "0.3.0rc2")
self.assertEqual(plan["version"], VERSION)
self.assertEqual(plan["channel"], "rc")

def test_full_uv_lifecycle_is_idempotent_and_preserves_research_state(self) -> None:
Expand Down Expand Up @@ -240,7 +247,8 @@ def test_full_uv_lifecycle_is_idempotent_and_preserves_research_state(self) -> N
sentinel = research / "existing.txt"
sentinel.write_text("preserve me\n", encoding="utf-8")

wheel = artifacts / "benchwork.whl"
wheel_name = "benchwork_arcana-0.3.0rc3-py3-none-any.whl"
wheel = artifacts / wheel_name
wheel.write_bytes(b"fake exact wheel")
plugin = artifacts / "plugin.tar.gz"
subprocess.run(
Expand All @@ -263,13 +271,13 @@ def test_full_uv_lifecycle_is_idempotent_and_preserves_research_state(self) -> N

fake_curl = fake_bin / "curl"
fake_curl.write_text(
"""#!/usr/bin/python3
f"""#!/usr/bin/python3
import os, pathlib, shutil, sys
args = sys.argv[1:]
output = pathlib.Path(args[args.index("--output") + 1])
url = args[-1]
name = "release-manifest.json" if url.endswith("release-manifest.json") else pathlib.Path(url).name
mapping = {"benchwork.whl": "benchwork.whl", "plugin.tar.gz": "plugin.tar.gz"}
mapping = {{{wheel_name!r}: {wheel_name!r}, "plugin.tar.gz": "plugin.tar.gz"}}
source = pathlib.Path(os.environ["FAKE_ARTIFACTS"]) / mapping.get(name, name)
shutil.copyfile(source, output)
""",
Expand Down Expand Up @@ -322,7 +330,7 @@ def test_full_uv_lifecycle_is_idempotent_and_preserves_research_state(self) -> N
"sh",
str(ROOT / "install.sh"),
"--version",
"0.3.0rc2",
VERSION,
"--backend",
"uv",
"--install-dir",
Expand Down
2 changes: 1 addition & 1 deletion tests/plugin/test_plugin_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_manifest_marketplace_and_components_agree(self) -> None:
entry = next(item for item in marketplace["plugins"] if item["name"] == "benchwork")
self.assertEqual(marketplace["name"], "benchwork-local")
self.assertEqual(manifest["name"], "benchwork")
self.assertEqual(manifest["version"], "0.3.0-rc.2")
self.assertEqual(manifest["version"], "0.3.0-rc.3")
self.assertEqual(entry["source"]["path"], "./plugins/benchwork")
self.assertEqual(entry["policy"]["installation"], "AVAILABLE")
self.assertEqual(entry["policy"]["authentication"], "ON_INSTALL")
Expand Down
2 changes: 1 addition & 1 deletion tests/test_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class DistributionTest(unittest.TestCase):
def test_package_and_project_versions_match(self) -> None:
project = loads((Path(__file__).parents[1] / "pyproject.toml").read_text(encoding="utf-8"))
self.assertEqual(benchwork.__version__, project["project"]["version"])
self.assertEqual(benchwork.__version__, "0.3.0rc2")
self.assertEqual(benchwork.__version__, "0.3.0rc3")

def test_init_creates_both_versioned_registries(self) -> None:
with tempfile.TemporaryDirectory() as directory:
Expand Down