# Publish

**Status, 2026-09-04.** This file used to open "Nothing in this directory has been published." That
has been false since 2026-08-18, when nine GitHub repositories, two Hugging Face Spaces and two
dataset cards were pushed (`OSS_RECONCILIATION.md` §1). Verified again on 2026-09-04 by comparing
git blob shas file by file: all nine repositories are **byte-identical to this tree** — 0 differ,
0 only-local, 0 only-remote — and both dataset cards and both Space pages match by sha256.

What is genuinely unpublished is **§1, the PyPI uploads**: `minicheck` 0.4.1, `protocol-bench`
1.1.1, `failclosed` 0.2.1 and `polyfrac` 0.2.1 are built and `twine check`-clean on disk while the
index still serves 0.4.0 / 1.1.0 / 0.2.0 / 0.2.0.

Sections 2 and 3 are therefore **re-publish** procedures, not first publishes. Their two rules still
hold and are the reason they are written down. Run them in the order given.

**Order matters in one place only:** PyPI before the Spaces. The Spaces vendor wheels built from
this tree at `0.4.1` / `1.1.1`, and their pages tell a reader `pip install protocol-bench`. Publish
the index first and the two agree; publish the Spaces first and they sit a patch ahead for as long
as the gap lasts. Nothing breaks either way — the wheels are self-contained — but the first order is
the honest one.

**One rule that is easy to get wrong.** Never publish a Space or a repo by replacing its directory.
Both Spaces carry a `.gitattributes` (the LFS rules for the `.whl` files) that has never existed in
this tree, and each published repo carries `.github/workflows/` — a wholesale copy deletes them. The
commands below use `rsync --delete` *from a clone*, which was checked against all nine repositories:
zero deletions, `.github/workflows/` intact. For the Spaces they copy files in without `--delete` at
all, so `.gitattributes` and the superseded `app_lite.py` survive.

---

## 1 — PyPI (4 packages, all version bumps)

Each carries new `[project.urls]` metadata, so their PyPI pages finally link back to the repository.
That change is the reason for the bump; the code is otherwise the released code.

```bash
cd oss
python -m twine upload minicheck/dist/*        # 0.4.0 -> 0.4.1
python -m twine upload protocol-bench/dist/*   # 1.1.0 -> 1.1.1
python -m twine upload failclosed/dist/*       # 0.2.0 -> 0.2.1
python -m twine upload polyfrac/dist/*         # 0.2.0 -> 0.2.1
```

Then refresh the sentences that describe the index, and commit the result:

```bash
python tools/index_names.py --write
# and in each of the four READMEs, bump the "Published on PyPI as `<name>` X.Y.Z" line to the
# version you just uploaded. `tests/test_install_line.py` fails until you do — that is its job.
```

`minicheck-mcp` and `specforge` are **not** on this list. `minicheck-mcp` has never been uploaded;
one reason, not the three this tree used to give (see `README.md`).
`specforge` **cannot be uploaded under that name**: it belongs to
[SGLang's SpecForge](https://pypi.org/project/specforge/), so `pip install specforge` already
succeeds and installs an unrelated project. That needs a rename before any index release.

## 2 — GitHub (9 repositories)

`rsync --delete` from a fresh clone. Verified across all nine: no published file is removed,
`.github/workflows/` survives, and `build/`, `dist/` and `*.egg-info/` are never published.

```bash
cd /tmp && rm -rf publish && mkdir publish && cd publish
OSS="<path to>/oss"

for r in failclosed minicheck minicheck-action minicheck-mcp polyfrac \
         protocol-bench protocol-bench-action specforge verification-docs; do
  loc="$r"; [ "$r" = "protocol-bench-action" ] && loc="action-repo"
  git clone "https://github.com/nickharris808/$r.git"
  rsync -a --delete --exclude='.git/' --exclude='build/' --exclude='dist/' \
        --exclude='*.egg-info/' --exclude='__pycache__/' --exclude='.ruff_cache/' --exclude='.pytest_cache/' \
        "$OSS/$loc/" "$r/"
  # stage explicit paths — never `git add -A`; other sessions share these trees
  (cd "$r" && git status --short && git status --porcelain | awk '{print $2}' | xargs -r git add --)
done
```

Then, per repository, after reading the status:

```bash
cd <repo> && git commit -m "install claims derived from PyPI; badges that can go red" && git push
```

Watch the first CI run. Each repository now runs `tests/test_install_line.py`, which reaches PyPI —
so a red build there is a claim that stopped being true, not flakiness.

## 3 — Hugging Face Spaces (2)

Both are currently serving a Python traceback to every visitor. This is the change that fixes that.

```bash
cd /tmp && rm -rf hf && mkdir hf && cd hf
OSS="<path to>/oss"

for s in protocol-bench-demo specforge-leaderboard; do
  git clone "https://huggingface.co/spaces/nickh007/$s"
  # no --delete: .gitattributes holds the LFS rules and app_lite.py is the superseded build.
  # Neither exists in this tree and neither should be removed by a publish.
  rsync -a --exclude='__pycache__/' --exclude='.ruff_cache/' --exclude='.pytest_cache/' "$OSS/spaces/$s/static/" "$s/"
  (cd "$s" && git status --short && git status --porcelain | awk '{print $2}' | xargs -r git add --)
done
```

Then, per Space:

```bash
cd <space> && git commit -m "replace the gradio-lite shell: pinned Pyodide, vendored closure" && git push
```

Open each one afterwards. Within about five seconds the status line should read
**"Ready — the checker is running in this page"**, and a verified result is on screen before that.
If it reads **failed**, the message names the cause and the static result stays readable — that is
the designed behaviour, not a silent blank page.

Two files remain live that this tree no longer produces: `app_lite.py` (the old gradio-lite app) and
the `minicheck-0.4.0` / `protocol_bench-1.1.0` wheels. Deleting them is optional cleanup, not part
of the fix, and unpublishing is harder than publishing — so they are left alone here.

## 4 — Hugging Face datasets (2)

```bash
cd /tmp/hf
git clone "https://huggingface.co/datasets/nickh007/protocol-bench" ds-protocol-bench
cp "$OSS/protocol-bench/dataset/README.md" ds-protocol-bench/README.md
(cd ds-protocol-bench && git add -- README.md && git diff --cached && git commit -m "install line: the package is on PyPI" && git push)

git clone "https://huggingface.co/datasets/nickh007/specforge" ds-specforge
cp "$OSS/specforge/dataset/README.md" ds-specforge/README.md
(cd ds-specforge && git add -- README.md && git diff --cached && git commit -m "warn that the PyPI name belongs to another project" && git push)
```

The `specforge` card had no local source before this change — it existed only on the Hub, which is
why it drifted unnoticed. `oss/specforge/dataset/README.md` is now that source.

---

## What is deliberately not here

Nothing is made private. Every artifact in this directory earned its place: six packages that a
stranger can install and run, two benchmarks whose subject is the *field's* failure modes rather
than ours, two browser demos that execute a real model checker client-side, two GitHub Actions and a
documentation site. The one artifact worth a second look is `polyfrac` — exact rational arithmetic
is genuinely useful but sits furthest from the portfolio's argument — and the recommendation there
is to leave it public and let the docs site keep framing it, not to hide it.
