fix(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid
, '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(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid
, '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(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid
, '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(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid
, '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(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid
, '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(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid
, '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(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid
, '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(coil): restore the release, and make two of its checks capable of failing - #97

Merged
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks
Aug 12, 2026
Merged

fix(coil): restore the release, and make two of its checks capable of failing#97
radroid merged 1 commit into
mainfrom
coil/fix-release-bundle-checks

Conversation

@radroid

Copy link
Copy Markdown
Owner

Releases have been red since #53 landed. Build 106 was the last one published — every run after it failed before publishing anything, so #95 and #96 have not shipped to any user.

Three defects, all in the new bundle-size checks.

1. The exclusions CLI wrote nothing on Windows

All three new scripts guarded their CLI entry point with:

if(import.meta.url===`file://${process.argv[1]}`){

That is false on Windows. process.argv[1] is a native path there, so the template builds file://D:\a\... while import.meta.url is file:///D:/a/... — drive letter after three slashes, forward separators. Verified:

win builds: file://D:\a\t3code\t3code\scripts\coil\desktop-file-exclusions.mjs
win actual: file:///D:/a/t3code/t3code/scripts/coil/desktop-file-exclusions.mjs

It works on macOS and Linux only because an absolute POSIX path already begins with /, which is why it passed review and passed on one runner. So node desktop-file-exclusions.mjs parsed, ran nothing, wrote nothing, exited 0 — and the release step correctly refused an empty list.

The same guard is in verify-desktop-bundle.mjs, which the release invokes as a CLI. On Windows that verification would have exited 0 without running, even after the rest of this was fixed. All three now use pathToFileURL.

2. app.asar was never under release/

Both checks searched release/. The build copies only files out of its staging dir — if (!stat || stat.type !== "File") continue in build-desktop-artifact.ts — and the macOS .app and Windows win-unpacked/ are directories, so the trees holding app.asar are never copied there.

Worse, the staging dir was a scoped temp dir, deleted the moment the build process exits. There was nothing to find anywhere on disk.

T3CODE_DESKTOP_KEEP_STAGE (which Config.boolean accepts as "1") keeps it; one new step resolves it through os.tmpdir() rather than assuming /tmp or %TEMP%, folding backslashes so git-bash's find can take it; a cleanup step drops it again before the upload.

3. The app-update.yml guard had never been able to fail

Same wrong directory, opposite outcome. It searched release/ for a file that only exists inside the .app, found nothing, printed "No app-update.yml packaged", and passed — every run, unconditionally.

It was never checking the claim it is the only evidence for: that this fork's toast is the sole update surface and upstream's electron-updater is off. A check that fails loudly costs one red build; a check that cannot fail costs nothing until the day it was supposed to catch something.

Tests

Cover the shape that broke rather than the line: invoked as a CLI, these scripts must actually do something. One asserts non-empty stdout, one asserts a no-argument verify run does not exit 0.

They cannot run on Windows here — this fork has no Windows CI runner — so that platform's correctness rests on pathToFileURL being the right primitive, not on a green check. That asymmetry is the same one that let this ship, and it is stated in the test file rather than left implicit.

Verification

  • locate/cleanup logic exercised locally against a simulated two-stage tree, including a path with spaces; picks the newest stage
  • YAML parses; Config.boolean confirmed to accept "1" from Effect's TrueValues
  • new CLI tests pass; the rest of desktop-bundle-size.test.ts unchanged and passing

The real proof is the release run on merge, which is the one thing that cannot be tested beforehand.

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81df0dd3-4baf-4d3c-a0dc-f5f23385fd03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… failing
Releases have been red since #53 landed. Build 106 was the last one published;
every run after it failed before publishing anything. Three defects, all in the
new bundle-size checks, and the third is the one worth reading twice.
1. The exclusions CLI wrote nothing on Windows.
All three scripts guarded their CLI entry point with
`import.meta.url === \`file://${process.argv[1]}\``. That is false on Windows,
where argv[1] is a native path: the template builds `file://D:\a\...` while
`import.meta.url` is `file:///D:/a/...`. It works on macOS and Linux only
because an absolute POSIX path already begins with `/`, which is why it
passed review and passed on one runner.
So `node desktop-file-exclusions.mjs` parsed, ran nothing, wrote nothing and
exited 0, and the release step correctly refused an empty exclusion list.
The same guard is in verify-desktop-bundle.mjs, which the release invokes as
a CLI — so on Windows that verification would have exited 0 without running
even once the rest of this was fixed. Fixed with `pathToFileURL`, which is
what Node provides for this conversion.
2. app.asar was never under release/.
Both checks searched `release/`. The build copies only FILES out of its
staging dir — `if (!stat || stat.type !== "File") continue` — and the macOS
`.app` and Windows `win-unpacked/` are directories, so the trees holding
app.asar are never copied there. The staging dir was also a SCOPED temp dir,
deleted when the build exits, so there was nothing to find anywhere.
T3CODE_DESKTOP_KEEP_STAGE keeps it, one new step resolves it through
`os.tmpdir()` rather than assuming /tmp or %TEMP%, and a cleanup step drops
it again before the upload.
3. The app-update.yml guard had never been able to fail.
Same wrong directory, opposite outcome. It searched release/ for a file that
only exists inside the .app, found nothing, printed "No app-update.yml
packaged", and passed — every run, unconditionally. It was never checking the
claim it is the only evidence for: that this fork's toast is the sole update
surface and upstream's electron-updater is off.
A check that fails loudly costs one red build. A check that cannot fail costs
nothing until the day it was supposed to catch something.
Tests cover the shape that broke rather than the line: invoked as a CLI, these
scripts must actually do something. They cannot run on Windows here — this fork
has no Windows CI runner — so that platform's correctness rests on pathToFileURL
being the right primitive, not on a green check. That asymmetry is the same one
that let this ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radroid
radroidforce-pushed the coil/fix-release-bundle-checks branch from 9875fcd to 5ee76a8CompareAugust 12, 2026 17:03
@radroid
radroid merged commit deffbbc into mainAug 12, 2026
2 checks passed
@radroid
radroid deleted the coil/fix-release-bundle-checks branch August 12, 2026 17:13
radroid added a commit that referenced this pull request Aug 14, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 17, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…lly disabled
The check restored in #97 failed on its first real run, on both platforms, and it
was right to. `app-update.yml` is in the bundle:
.../T3 Coil (Alpha).app/Contents/Resources/app-update.yml
.../win-unpacked/resources/app-update.yml
It is in the installed build too — 0.0.33-coil.105 on disk carries
`owner: radroid / repo: t3code / provider: github / releaseType: release`. So has
every build before it. This is not a regression #97 introduced; it is what the
old check could not see.
WHY IT WAS NEVER DISABLED. The workflow set `GITHUB_REPOSITORY: ""` and its
comment claimed that made `resolveGitHubPublishConfig` return undefined. Actions
refuses to let a workflow set any `GITHUB_`-prefixed variable, so the line is
inert. The build read the runner's real `radroid/t3code`, resolved a publish
config, and electron-builder wrote the feed. The shipped file proves the path
taken: those owner/repo values cannot come from any other branch of that
function.
`T3CODE_DESKTOP_UPDATE_REPOSITORY` is the fork's own hook, is read FIRST, and is
not reserved. Set to `disabled` it short-circuits `GITHUB_REPOSITORY` before that
is read, fails the `owner/repo` split, and returns undefined — the no-publish
path. No publish config, no `app-update.yml`, `hasUpdateFeedConfig` false, and
upstream's electron-updater switches itself off. Which is what the empty string
was always meant to do.
WHY THIS WAS URGENT RATHER THAN MERELY WRONG. `releaseType: release` kept the
second updater inert by accident: every fork release was a GitHub prerelease, so
it could never find anything to install. #96 has just made releases full releases
with a real Latest pointer. The next successful publish would have been the first
thing that dormant updater could see, in every installed build.
Tests pin both directions — that the hook produces no config, and that
GITHUB_REPOSITORY alone still produces one, so nobody later reads the hook as
redundant. They live in the fork's test file; the function is exported, so this
costs no seam row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
radroid added a commit that referenced this pull request Aug 18, 2026
…annot see (#102) (#106)
Upstream's Windows server.asar split (this sync) broke the release's
bundle-verify gate both ways at once: the server graph silently stopped
being scanned, and node-pty — mentioned only inside main.cjs's embedded
WSL heredoc scripts, loadable from the sidecar where it genuinely lives —
was reported unresolvable, failing a shippable build (release run
31837711136, publish skipped).
The view now merges a sibling resources/server.asar (+ .unpacked) into
the packaged-file map, so sidecar bundles are scanned again and sidecar
packages resolve. And every FIRST_PARTY_BUNDLE_DIRS entry must contribute
at least one scanned bundle — a packaging-topology change that hides a
layer is now an error instead of an empty green result, which is the
'guard that cannot fail' shape #97 already taught us once.
Also corrects the three comments still teaching the disproven
GITHUB_REPOSITORY="" mechanism (main.ts, UpdateToast.tsx) and the
workflow step's stale Windows-topology claim.
Fixes#102.
Co-authored-by: Claude Fable 5 <noreply@anthropic.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

@radroid