Skip to content

fix: keep new bridge asset tags npm-shaped instead of appending -N - #131

Merged
leehack merged 1 commit into
mainfrom
fix/npm-shaped-bridge-tags
Sep 24, 2026
Merged

leehack merged 1 commit into
mainfrom
fix/npm-shaped-bridge-tags

Conversation

@leehack

@leehack leehack commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Problem

On 2026-09-24 one orchestrator scan dispatched two pipelines at bridge build 5e8cc64:

  • Native v0.4.1-1 claimed v0.1.50. This was a redundant pipeline: bridge chore: build ordinary CI against llama.cpp v0.5.0 #129 changed only a build input, so its bytes are identical to v0.1.49.
  • Native v0.5.0 got v0.1.50-1 from select_next_release_target, because v0.1.50 was already claimed.

npm and semver order v0.1.50-1 as a prerelease below v0.1.50. The llama.cpp v0.5.0 assets would therefore have sorted below a redundant v0.4.1 rebuild, and llamadart pins only plain vMAJOR.MINOR.PATCH bridge tags, so it could not have adopted them.

There was a second effect. While the v0.1.50-1 claim was live, _claimed_rebuilds_of stopped the byte-identical v0.4.1-1 candidate from ending as satisfied_by_identical_release. Scan 36029945708 therefore dispatched qualification to publish a duplicate v0.1.50.

Both runs were cancelled: candidate 36028710227 (v0.1.50-1) and qualification 36030026422 (v0.1.50).

Fix

  • Tag selection (select_next_release_target): published and claimed tags both set the version floor. A collision takes the next free patch version, always with rebuild 0. A new tag is never lower than a published tag or a live claim. A released claim stops counting, so its version may be taken again or stay unused.
  • Contract (release_contract.validate_new_release_identity): a new release must have rebuild 0. The validate-release command uses this check, and bridge_candidate.yml and publish_assets.yml both run that command, so a -N tag can be neither built nor published.
  • Reading history stays permissive: validate_release_identity, PipelineBinding recovery from run names, published manifests and release_publication_state still accept earlier -N tags such as v0.1.47-1.
  • README and CONTRIBUTING state the rule. Native releases keep -N.

scripts/release_contract.py is a governed path, so merging this gives the bridge a new build identity. That un-blocks native v0.5.0: the old binding is stuck on v0.1.50-1 because a cancelled candidate pins its tag.

After merge

Run a plain stable scan (native_release_tag left empty) so tags follow native order:

  • v0.4.1-1 takes v0.1.50. It should end as satisfied_by_identical_release, and the number stays unused.
  • v0.5.0 publishes as v0.1.51.

Dispatching v0.5.0 on its own to get v0.1.50 is unsafe. If the v0.4.1-1 bytes differed, native-order publication would publish v0.1.51 first, and v0.1.50 would then fail the forward-only check.

Tests

The new and updated tests fail against the old code (an independent review ran them on a git archive copy of the base).

  • Orchestrator: collision moves to the next patch; claims set the floor; historical -N sets the floor but is never emitted; seed collision; and 4 pipeline tests that used to expect -1 dispatches.
  • Contract: validate_new_release_identity accepts plain tags and rejects v0.1.50-1, v0.1.47-1 and b10600-1, while history still parses. The validate-release command accepts v0.1.50 and rejects v0.1.50-1.

Local verification

  • python3 -m unittest discover -s scripts -p '*_test.py' </dev/null: 421 tests, OK.
  • python3 scripts/verify_ci_reliability.py </dev/null: passed.
  • npm run check:js and the CONTRIBUTING lightweight contract list pass.

Review notes

An independent review found nothing blocking. It confirmed:

  • no remaining path emits -N;
  • the stricter publish check strands no in-flight or recoverable publication;
  • in bridge_candidate.yml, validate-release runs before generate_release_manifest.py.

I fixed its doc finding, the claim-reuse wording. It also noted that _claimed_rebuilds_of can no longer fire for new claims; it is harmless and left in place.

When two pipelines shared one bridge build, select_next_release_target gave
the second a rebuild tag. On 2026-09-24 the native v0.4.1-1 pipeline claimed
v0.1.50 and the native v0.5.0 pipeline got v0.1.50-1. npm orders v0.1.50-1 as
a prerelease below v0.1.50, so the newer llama.cpp v0.5.0 assets would have
sorted below a redundant v0.4.1 rebuild, and consumers that pin plain
vMAJOR.MINOR.PATCH (llamadart) could not adopt it. While the v0.1.50-1 claim
was live, _claimed_rebuilds_of also kept the byte-identical v0.4.1-1 candidate
from ending as satisfied_by_identical_release, so it went to qualification to
publish a duplicate v0.1.50. Both runs were cancelled.

- select_next_release_target uses published and claimed tags as the version
  floor and takes the next free patch version on a collision, always with
  rebuild 0. A new tag is never lower than a published tag or a live claim.
- release_contract.validate_new_release_identity requires rebuild 0. The
  validate-release command, which bridge_candidate.yml and publish_assets.yml
  run, now uses it, so a -N tag cannot be built or published. Reading history
  (validate_release_identity, run-name binding recovery, manifests) still
  accepts earlier -N tags such as v0.1.47-1.
- README and CONTRIBUTING describe the rule.
@leehack
leehack merged commit 6ed6213 into main Sep 24, 2026
5 checks passed
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