From 3b7f539fba0bc2bc2e3d2c01f42d7d0960ed42d4 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 8 Sep 2026 15:50:00 -0400 Subject: [PATCH] chore(release): pin npm for trusted publishing, normalise redacted whitespace --- .github/workflows/release.yml | 7 +++++++ fallback.js | 10 +++++----- test/access-directions.test.mjs | 2 +- test/fallback.test.mjs | 12 ++++++------ 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e68c58a..44ccf9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,6 +53,13 @@ jobs: steps: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: {node-version: "22", registry-url: "https://registry.npmjs.org"} + # npm's OIDC trusted publishing needs npm 11.5.1 or newer; Node 22 still bundles + # npm 10, which performs no OIDC exchange at all. The publish then goes out + # unauthenticated and the registry answers `404 Not Found - PUT`, which reads + # exactly like a missing package rather than a missing credential -- the failure + # that sent 1.0.0 out by hand, permanently without provenance. + - run: npm install -g npm@^11.5.1 + - run: npm --version - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: tarball diff --git a/fallback.js b/fallback.js index 8528dc4..c5ef266 100644 --- a/fallback.js +++ b/fallback.js @@ -22,13 +22,13 @@ const UNSUPPORTED={ request:[], configuration:[], // `hull_vertices`, `compression_ratio` and `max_compression_pressure_kpa` left this list - // in , the last engine to gain both the solver behaviour and the independent + // in, the last engine to gain both the solver behaviour and the independent // validation the staged rollout requires. item:[], // `pallet_overhang_limit` was reserved in the schema by at the 1.1.0 contract // freeze and is refused everywhere until an engine implements it from a request: a field // a caller can set and the solver ignores is worse than a refusal. - // `access_directions` left this list in , which wired the reserved field through + // `access_directions` left this list in, which wired the reserved field through // to the stop-accessibility rule in all four engines at once. container:['pallet_overhang_limit'], obstacle:[], @@ -37,7 +37,7 @@ const UNSUPPORTED={ // caller that spells the default out must be served, not refused. What is unimplemented // is a *value*, and the refusal names it -- packing a `convex_hull` item as its bounding // box would return a plan that looks valid and does not physically fit. - // Empty since : this engine implements every value the schema defines. The guard + // Empty since: this engine implements every value the schema defines. The guard // stays because the next reserved value will need it. shapeType:[], }; @@ -1603,7 +1603,7 @@ if(solverAlias===null&&requestedSolvers.length){ winner.termination=aggregateTermination(starts); winner.algorithm=withPortfolioEffort(winner,runs); const alternativeLimit=Math.max(0,(req.configuration?.alternatives??3)-1); - // The sentinel is a search device, never an answer -- alternatives included ( review). + // The sentinel is a search device, never an answer -- alternatives included (review). winner.alternatives=runs.filter((run,index)=>index!==winnerIndex&&!run.unpriceableDetail).sort((a,b)=>compareScore(a.score,b.score)).slice(0,alternativeLimit); return finalizeOutermost(winner); } @@ -2275,7 +2275,7 @@ for(const c of packed){scoreCost+=c.tmpl.cost_minor??0; // to invent -- so the refusal fires, but once, at the outermost frame, on the packing // actually selected for return: a portfolio sibling with a priceable answer must not be // aborted by this run's refusal. Rust, Python and PHP refuse at the same single choke -// point ( second review). The detail rides the result as a non-enumerable property +// point (second review). The detail rides the result as a non-enumerable property // below, a search device that never serializes. let unpriceableDetail=null; if(objective==='lowest_landed_cost')for(const c of packed){ diff --git a/test/access-directions.test.mjs b/test/access-directions.test.mjs index 67d8e14..e659318 100644 --- a/test/access-directions.test.mjs +++ b/test/access-directions.test.mjs @@ -73,7 +73,7 @@ test('every legal direction is accepted', () => { assert.ok(packFallback(request(['-z', '+z', '-y', '+y', '-x', '+x'])).status); }); -// A container that names no doors is the pre- default: the rule is inert, not the +// A container that names no doors is the pre-default: the rule is inert, not the // container sealed. `[]` is a caller saying "no doors stated" rather than a malformed // request, so it has to behave exactly like the absent field — otherwise the two spellings // of one default diverge. diff --git a/test/fallback.test.mjs b/test/fallback.test.mjs index 3256103..edd2c22 100644 --- a/test/fallback.test.mjs +++ b/test/fallback.test.mjs @@ -1241,7 +1241,7 @@ test('an unpriceable trial cannot win the round on placing more items', () => { // The snug box holds both bricks but bills 2000 g against a ladder that stops at // 1500 g; the per-unit box ships one brick at 100. A round key that ranked progress // first committed the snug box and refused this request, while Rust, Python and PHP - // ship it in two per-unit boxes at 200 ( second review). + // ship it in two per-unit boxes at 200 (second review). const req = request( [cube('brick', 100, { weight: '1000 g', quantity: 2 })], [ @@ -1294,7 +1294,7 @@ test('the compact lattice path no longer commits to an unpriceable container (qu // Found by adversarial review: the compact lattice path scored `lowest_landed_cost` // with the same billed-weight proxy the general path uses, but never stood down for // it the way it already does for a registered policy rule -- so it could commit to - // one container with nothing to correct that choice once made. ` `'s + // one container with nothing to correct that choice once made. ``'s // homogeneous-block quality search, unlike the compact path or the default // `balanced` general search, prices every candidate container exactly rather than by // proxy, so it is the one shape that already gets this right; this pins that it stays @@ -1376,7 +1376,7 @@ test('a portfolio returns the priceable sibling instead of propagating a child r // The engine used to throw the no-published-price refusal inside the extreme_points // child run, aborting the request its layer sibling could price at 2400. The refusal // now fires once, at the outermost frame, on the packing actually selected for - // return -- the choke point Rust, Python and PHP already refuse at ( second + // return -- the choke point Rust, Python and PHP already refuse at (second // review) -- so the priceable sibling wins on the ordinary score comparison. const result = packFallback(splitPortfolio(2600)); assert.equal(result.status, 'feasible'); @@ -1388,7 +1388,7 @@ test('a portfolio returns the priceable sibling instead of propagating a child r test('alternatives never surface the unpriceable sentinel', () => { // The losing extreme_points run carries score[1] = MAX_SAFE_INTEGER. The sentinel is - // a search device, never an answer -- alternatives included ( review): the run + // a search device, never an answer -- alternatives included (review): the run // is filtered out rather than offered as a packing costing 2^53-1 minor units. const filtered = packFallback(splitPortfolio(2600)); assert.equal(filtered.alternatives.length, 0); @@ -1491,7 +1491,7 @@ test('rebalancing refuses an input packing the tariff cannot price', () => { test('rebalancing applies the same landed-cost admission as packing', () => { // The current packing uses only `rated`, but `untabled` remains a request option. // Letting the direct rebalance API ignore it would make its contract weaker than - // packFallback and the native implementation ( second review). + // packFallback and the native implementation (second review). const base = request( [cube('parcel', 100, { weight: '500 g' })], [box('rated', 200, 200, 200, { rate_table: { weight_brackets_g: [2000], prices_minor: [500] } })], @@ -1777,7 +1777,7 @@ test('the objective lower bound matches Python on every corpus case', (t) => { }); test('a sum past the declared ceiling is refused rather than answered', () => { - // , at the same two inputs Python, PHP and Rust assert. The ceiling is declared + //, at the same two inputs Python, PHP and Rust assert. The ceiling is declared // rather than inherited: this engine's `Number` stops being exact past 2^53, PHP's integers // silently become doubles, Python's are unbounded and Rust's i128 wraps. If each refused at // its own limit, a caller would get a number from one engine and a refusal from another for