From 83e248a5a68c38bc77652fc7835fda5a7326108e Mon Sep 17 00:00:00 2001 From: mattshax Date: Tue, 15 Sep 2026 19:11:31 +0000 Subject: [PATCH 1/2] docs: change log for v1.60 --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d10ee0..487e3d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ Every version, newest first. Each entry is the description of the pull request that made the change, which is written as a change note when the change is made. +## v1.60 (2026-09-15) + +### A change log and release notes generated from pull request descriptions (#339) + +Versions were tagged but never published as GitHub Releases, so the change record existed only as PR titles. Every merged PR here carries a description written as a change note, so the notes for a version are those descriptions in order, and the change log is the same for every version at once. + +`scripts/release-notes.mjs` produces both from the tags and the gh CLI (`vX.Y` for one version, `--changelog` for all), mapping squash commits to PRs by the `(#N)` suffix and dropping attribution trailers. `CHANGELOG.md` is its output for all sixty versions to date. The README documents the release steps, and every existing tag is being published as a Release with these notes. + +Also makes the run-watch test deterministic: it slept a fixed span and asserted a count band, which flaked on the macOS runner; it now waits for the give-up point and then proves no further polls happen. + +### The provider probe no longer marks a healthy model family unavailable (#340) + +The provider probe pinged with `max_tokens: 1`. One model family rejects every spelling of a token cap (`max_output_tokens` explicitly, `max_tokens` and `max_completion_tokens` behind the gateway's masked 400; measured earlier on `gpt-5.6-sol`), so the ping failed twice on its own parameter and the probe reported a reachable, unlocked provider as down. Every model of the family showed `[unavailable]` and the banner told the user to unlock a key that was never locked. + +The ping now carries no cap. Cost is bounded by reading the streamed response only to its first frame, which is proof the provider answers, then aborting. A body that names a request parameter as the problem is classified as a reachable provider regardless of status, since a provider arguing about the request's shape is up. Locked and unavailable verdicts are unchanged. + +Verified live against the cap-rejecting family on the gateway: the verdict is now ok. Tests: parameter rejection reads as up with no second ping; a healthy stream is read to one frame; existing locked, unlock-url, masked-twice, and cache cases still pass. 184 server tests pass. + ## v1.59 (2026-09-12) ### A manual macOS job that builds GUFI and exercises its vector stack (#337) From 2a4f29af5e618b379945536a7a9c4fd899c6bc35 Mon Sep 17 00:00:00 2001 From: mattshax Date: Tue, 15 Sep 2026 19:12:07 +0000 Subject: [PATCH 2/2] test: neutral provider names in the probe fixtures --- server/test/providerProbe.test.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/test/providerProbe.test.mjs b/server/test/providerProbe.test.mjs index 842bd95..3656604 100644 --- a/server/test/providerProbe.test.mjs +++ b/server/test/providerProbe.test.mjs @@ -75,7 +75,7 @@ test('a provider that rejects the token-cap parameter is up, not unavailable', a // read as a reachable provider either way. reset() responses.push({ status: 400, text: '{"error":{"message":"{\\"detail\\":\\"Unsupported parameter: max_output_tokens\\"}","type":"error"}}' }) - const v = await probeProvider('me:army', 'me:army/gpt-5.6-luna-gov', 'k7') + const v = await probeProvider('me:vega', 'me:vega/model-a', 'k7') assert.deepEqual([v.ok, v.kind], [true, null]) assert.equal(calls.length, 1, 'no second ping needed for a parameter complaint') }) @@ -91,7 +91,7 @@ test('a healthy stream is read only to its first frame', async () => { calls.push({ url: String(url), body: init?.body ? JSON.parse(init.body) : null }) return { ok: true, status: 200, body, text: async () => frames.join('') } } - const v = await probeProvider('me:army', 'me:army/gpt-5.6-terra-gov', 'k8') + const v = await probeProvider('me:vega', 'me:vega/model-b', 'k8') assert.equal(v.ok, true) assert.ok(pulls <= 1, `read ${pulls} chunks; one frame is proof enough`) globalThis.fetch = async (url, init) => {