fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(release): stop the desktop version silently blocking every release - #96

Merged
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags
Jul 25, 2026
Merged

fix(release): stop the desktop version silently blocking every release#96
pitimon merged 1 commit into
mainfrom
fix/version-lockstep-and-release-tags

Conversation

@pitimon

Copy link
Copy Markdown
Owner

Closes the second half of the two governance gaps found after 0.39.42 shipped.

The tags weren't forgotten — they were impossible

Tags stopped at v0.39.38 while npm reached 0.39.42. The cause isn't a missed step:

  • release-dmg.yml is the only thing in this repo that creates a tag. Its first job runs gh release create "v<version>" --draft --generate-notes, it dispatches the Windows build in parallel, and its final job flips the release out of draft with --latest. One dispatch produces the tag, the .dmg and the .exe.
  • That workflow refuses to build when TokenTrackerBar/project.yml's MARKETING_VERSION disagrees with package.json — and release-windows.yml does the same for TokenTrackerWin.csproj's <Version>.
  • Both sat at 0.39.38. So every desktop release from 0.39.39 on would have failed at that gate. "Forgot to tag" and "couldn't build the apps" were one omission, not two.

Three files carry the version, all three hand-edited, and npm version touches only one.

Why nothing caught it

The check already existed — in the release workflows. A gate in a workflow nobody dispatches reports nothing. It went unnoticed for four releases.

scripts/validate-version-lockstep.cjs runs the same check in ci:local, where every PR shows it. That is the reasoning ci.yml already records for itself: the #18 dashboard redesign merged with failing tests because the gate ran only post-merge, so the gate moved in front of the merge. Same fix, different gate.

It collects each version literal separately, so the case release-dmg.yml documents in its own comment — one of the two project.yml targets bumped, shipping a DMG whose Info.plist advertised the previous version — also fails. A checkout without the desktop projects passes; the CLI is publishable alone.

scripts/sync-desktop-version.cjs rewrites them and is wired to npm's version lifecycle. Deliberately not described as automatic: this repo bumps with npm version --no-git-tag-version, which makes no commit, so npm never stages what a version hook edited — the files remain working-tree changes you must git add. The validator is the guarantee; the hook only saves typing. The checklist says so in those words.

Also in this change set (already pushed, not in this diff)

  • Tags v0.39.39, v0.39.40, v0.39.41 backfilled as annotated tags at their real release commits (7c105cb, 96479cc, 0ea200e), each recording why it was late. Deliberately git tags and not GitHub Releases: an assetless Release would take over /releases/latest, which the README now points at for desktop downloads.
  • enforce_admins enabled on main. Protection required ci:local but exempted admins, so the two direct release pushes reported Bypassed rule violations. Verified that ci.yml's job is literally name: ci:local and runs on: pull_request, so PRs still produce the required context and merges are unaffected — this PR is that test. Direct pushes to main can no longer satisfy it, which is the intent.

After this merges, release-dmg.yml can be dispatched for 0.39.42 to produce the tag and both desktop builds.

Test plan

  • npm run ci:local — exit 0, 833/833 (was 827; +6 here)
  • Validator proven against the real defect: on main as it stood it printed all three mismatches and exited 1; after sync-desktop-version.cjs, exit 0
  • Six tests over a temp-directory fixture, so the validator never edits the tree it validates: the historical 0.39.38-vs-0.39.42 drift, the one-target-missed case, a sparse checkout, sync idempotence, and that sync rewrites only the version and leaves the rest of each file intact
  • enforce_admins blocking a direct push is verified by config read-back and mechanism, not by an attempted push — I was not willing to land an unreviewed commit on main to prove it. The first real attempt will confirm.

The repo's tags stopped at v0.39.38 while npm went to 0.39.42. That looked
like four forgotten tags. It wasn't: `release-dmg.yml` is the only thing
that tags, and it refuses to build when `TokenTrackerBar/project.yml`'s
MARKETING_VERSION disagrees with package.json. That file — and
`TokenTrackerWin.csproj` — sat at 0.39.38, so a desktop release was
impossible from 0.39.39 onward. Forgetting to tag and being unable to build
the apps were the same omission.
Three files carry the release version and all three were hand-edited;
`npm version` touched only one. The existing gates live in the release
workflows, which report nothing when nobody dispatches them — so the drift
was invisible for four releases.
- validate-version-lockstep.cjs runs the same check in `ci:local`, where a
PR shows it. Same reasoning ci.yml already records for moving the test
gate in front of the merge rather than after it. It collects each version
literal separately, so the "only one of the two project.yml targets got
bumped" case — which once shipped a DMG advertising the previous version —
fails too. A checkout without the desktop projects is not a failure.
- sync-desktop-version.cjs rewrites them, wired to npm's `version`
lifecycle. Deliberately NOT described as automatic: this repo bumps with
`npm version --no-git-tag-version`, which makes no commit, so npm never
stages what the hook edited. The files still need `git add`. The
validator is the guarantee; the hook just saves typing.
- Both desktop projects moved to 0.39.42, so the apps can be cut again.
This does not claim a desktop build ships with every npm release — the
README is explicit that they are cut less often. It keeps the version
recorded in *source* in step, which is what makes a release possible at any
time instead of a version bump away.
@pitimon
pitimon merged commit 58b8f92 into mainJul 25, 2026
1 check passed
@pitimon
pitimon deleted the fix/version-lockstep-and-release-tags branch July 25, 2026 02:30
@pitimonpitimon mentioned this pull request Jul 25, 2026
6 tasks
pitimon added a commit that referenced this pull request Jul 25, 2026
Ships #98 (issue #97): GitHub Copilot quota now reports the premium-request
count — "158/300", or "142/300" in Remaining mode — instead of a percentage
the reader has to convert. The numbers were already being fetched from
api.github.com and discarded after one division.
The independent Codex QA gate returned SHIP: NO on the first cut and all
three findings were real: the bar and its caption could be drawn from
different fields and disagree (70% beside 228/300, which is 76%); a null
`remaining` coerced to zero and reported the whole allowance consumed; and
the dashboard clamped only one end, so an unclamped payload could render
"312/300" in either mode. Fixed, each with a regression test built from the
exact reproduction.
First release where the `version` lifecycle hook from #96 did its job: the
bump synced TokenTrackerBar/project.yml and TokenTrackerWin.csproj, which is
what made 0.39.39–0.39.42 unable to cut a desktop build.
prepublishOnly re-vendored the LiteLLM seed: 2,522 models, no rate changes,
only _meta.generated_at moved.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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

@pitimon