Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
eeda2cb
feat(mobile): thread header minimizes on the Duo while reading back t…
juliusmarminge Oct 23, 2026
c8c9ccf
fix(web): center pull request timeline actions (#24259)
PixPMusic Oct 23, 2026
cd69df4
fix(server): find the resource monitor when the server runs from its …
yordis Oct 23, 2026
7beaa01
fix(pull-requests): list filters narrow on every host that can (#23922)
juliusmarminge Oct 23, 2026
3046545
fix(pull-requests): a filtered list fills its page instead of waiting…
juliusmarminge Oct 23, 2026
22dc1da
feat(pull-requests): label and author filters search the host (#23924)
juliusmarminge Oct 23, 2026
e01c399
fix(mobile): chat and composer chips stay readable in light mode (#22…
flamboh Oct 24, 2026
5e3ad56
fix(web): review thread avatars no longer clip in collapsed activity …
flamboh Oct 24, 2026
52628d8
ci: close "needs more info" issues after 7 days without a reply (#22628)
juliusmarminge Oct 24, 2026
33f1fe5
fix(server): Codex accounts show ChatGPT's current plan names (#24200)
flamboh Oct 24, 2026
bf5ffe8
fix(observability): desktop dev version and web environment match the…
yordis Oct 24, 2026
f252ed0
fix(observability): custom resource attributes use the t3code prefix …
yordis Oct 24, 2026
6c21798
fix(mobile): Home and pull request lists let UIKit own their top inse…
juliusmarminge Oct 24, 2026
4fef307
fix(mobile): keep the Duo's Home and pull request headers fixed (#24295)
juliusmarminge Oct 24, 2026
7d87be3
fix(server): interrupts started by an agent no longer say "by user" i…
juliusmarminge Oct 24, 2026
ff1f6fd
feat(relay): deploy on-demand preview stages for labeled PRs
juliusmarminge Oct 24, 2026
26c3625
fix(mobile): HTML links in pull request comments open when tapped (#2…
flamboh Oct 24, 2026
7de9535
perf(client): avoid full ordering scans for streamed replacements (#2…
juliusmarminge Oct 24, 2026
5b55036
fix(server): a fork's first /compact compacts its inherited history (…
juliusmarminge Oct 24, 2026
70bd962
fix(server): agent recordings honor the browser frame rate setting (#…
juliusmarminge Oct 24, 2026
d31269e
fix(client-runtime): Stop clears commands already finished on the ser…
juliusmarminge Oct 24, 2026
79e392c
fix(web): SSH environments on an older reused remote server explain t…
juliusmarminge Oct 24, 2026
f416175
fix(web): keep usage-limit wake action visible (#23803)
juliusmarminge Oct 24, 2026
3f3a1bf
fix(server): reject pairing with an incompatible server protocol (#23…
juliusmarminge Oct 24, 2026
d14ace8
fix(mobile): Live Activity stops shouting the running-agent count (#2…
juliusmarminge Oct 24, 2026
c893353
feat(usage): include Muse Code session history (#22443)
Wiktor102 Oct 24, 2026
91455de
feat(mobile): add Usage and Settings to workspace navigation (#23841)
PixPMusic Oct 24, 2026
c14cda1
fix(mobile): keep iPad workspace columns aligned while resizing (#24182)
PixPMusic Oct 24, 2026
45cedfe
fix(mobile): no-project threads show a chat bubble instead of a monog…
flamboh Oct 24, 2026
940e3fa
fix(mobile): stabilize Duo search and portrait navigation (#24206)
PixPMusic Oct 24, 2026
b24e8ce
fix(mobile): the unfolded Duo keeps its header in portrait (#24339)
juliusmarminge Oct 24, 2026
2c592a1
fix(client-runtime): avoid duplicate handoff previews during startup …
Yash-Singh1 Oct 24, 2026
aaf4a90
fix(mobile): Usage and Settings use the glass header and the Duo rail…
juliusmarminge Oct 24, 2026
27e0c68
perf(server): coalesce superseded streaming text updates (#24267)
juliusmarminge Oct 24, 2026
fef851b
fix(server): stop flooding analytics with successful MCP tool calls (…
t3dotgg Oct 24, 2026
bb7783e
feat(search): Cmd+K finds the right thread first, with indexed messag…
t3dotgg Oct 24, 2026
9a85aac
refactor(source-control): forges parse their own change request URLs …
juliusmarminge Oct 24, 2026
3d4c753
refactor(source-control): forges recognise their own remotes and repo…
juliusmarminge Oct 24, 2026
7368bd3
refactor(source-control): drivers build their own services and own ho…
juliusmarminge Oct 24, 2026
39f6e2b
refactor(source-control): git and process behaviour comes from forge …
juliusmarminge Oct 24, 2026
233f6ea
refactor(source-control): pull request behaviour comes from forge cap…
juliusmarminge Oct 24, 2026
a6009fa
refactor(clients): pull request surfaces ask the forge instead of ass…
juliusmarminge Oct 24, 2026
326e08b
refactor(clients): forge settings, routing and icons come from defini…
juliusmarminge Oct 24, 2026
218e5c0
fix(clients): change requests use their host's own term (#23999)
juliusmarminge Oct 24, 2026
4d87110
test(source-control): forge behaviour is tested in its own package (#…
juliusmarminge Oct 24, 2026
814bd67
refactor(source-control): forge definitions live in shared/, not clie…
juliusmarminge Oct 24, 2026
6e79211
refactor(source-control): forges handle their own listing and SSH hos…
juliusmarminge Oct 24, 2026
d0e621e
fix(mobile): the question card uses the whole screen while the keyboa…
shivamhwp Oct 24, 2026
a46f80e
fix(web): screen readers hear which background thread finished, even …
shivamhwp Oct 24, 2026
7f7daad
fix(web): screen readers say which thread a shortcut switched to (#23…
shivamhwp Oct 24, 2026
d53873b
fix(mobile): Back from a sub-agent opened from its delegator returns …
shivamhwp Oct 24, 2026
79014e6
fix(server): migrate-dev-db no longer trips over webhook tables (#24404)
juliusmarminge Oct 24, 2026
71df573
fix(clients): GitCafe links show GitCafe's mark on mobile (#24405)
juliusmarminge Oct 24, 2026
0345e85
feat: compactions T3 Code starts read as one working row, and stay qu…
juliusmarminge Oct 24, 2026
ea41701
fix(source-control): GitCafe checks show each check's newest run (#24…
juliusmarminge Oct 24, 2026
8176b58
fix(html): honor preview light and dark mode (#24521)
Bil0000 Oct 24, 2026
bf5c483
chore(mobile): move Enriched Markdown to upstream's block renderers (…
juliusmarminge Oct 24, 2026
be1d066
fix(provider-cursor): finished Cursor turns no longer end in Provider…
shivamhwp Oct 24, 2026
3afb9b3
fix(server): server preview browser rejects the compromised eMudhra i…
juliusmarminge Oct 24, 2026
eea9895
chore(review): Macroscope skips generated code and recorded fixtures …
juliusmarminge Oct 24, 2026
909e4c9
fix(server): align delegated review follow-up guidance (#24560)
juliusmarminge Oct 24, 2026
3cc31ed
fix(server): websocket tickets name the route they open (#24554)
juliusmarminge Oct 24, 2026
76da264
perf(mobile): highlight code with native @pierre/highlights instead o…
juliusmarminge Oct 24, 2026
7fd2e09
fix(desktop): support Linux distro protocol launchers (#24591)
juliusmarminge Oct 24, 2026
c4fa462
fix(provider-acp): force-kill terminal commands that ignore SIGTERM (…
juliusmarminge Oct 24, 2026
843f4e3
fix(grok): invoke leading picked skills through native commands (#24605)
juliusmarminge Oct 24, 2026
553203c
fix(server): long agent turns no longer load whole when a thread open…
t3dotgg Oct 24, 2026
8bcef34
fix(server): analytics stop counting reconnects, retries, and dev ser…
t3dotgg Oct 24, 2026
5a969c1
fix(acp): idle agent errors fail their turns (#24615)
juliusmarminge Oct 25, 2026
0840bb2
fix(provider-acp): Grok monitors show as monitors in background work …
t3dotgg Oct 25, 2026
249b6ae
feat(server): devices on the same network reach background services o…
t3dotgg Oct 25, 2026
7769d70
fix(codex): bound pending requests after app-server stdout closes (#2…
juliusmarminge Oct 25, 2026
ce8d644
fix(cursor): discover built-in skills in the composer (#24595)
juliusmarminge Oct 25, 2026
97548db
fix(server): retry provider event writes after brief SQLite locks (#2…
juliusmarminge Oct 25, 2026
3eb8366
fix(server): settle inactive threads with persisted open PR links (#2…
juliusmarminge Oct 25, 2026
5848bc2
feat(mobile): Settings is a workspace tab, and tabs never open on an …
juliusmarminge Oct 25, 2026
5cf4995
feat(mobile): the folded Duo opens search from a search tab below the…
juliusmarminge Oct 25, 2026
95ffbc9
revert(mobile): take the folded Duo search tab back out for now (#24626)
juliusmarminge Oct 25, 2026
27805ef
fix(web): preview images outside the workspace (#24627)
juliusmarminge Oct 25, 2026
91ea0e1
fix(threads): Working holds only background work the agent waits on (…
t3dotgg Oct 25, 2026
fa1347d
fix(web): switched threads and Scroll to end land on the real end (#2…
t3dotgg Oct 25, 2026
14b828e
fix(web): attach DNG originals as generic files (#24625)
juliusmarminge Oct 25, 2026
cd2b681
fix(server): surface failed runtime request answer delivery (#24616)
juliusmarminge Oct 25, 2026
b8be6da
fix(preview): report navigation timeout accurately (#24356)
Ethereal49 Oct 25, 2026
945fd1d
fix(web): give PR row metadata room below the title (#24553)
juliusmarminge Oct 25, 2026
5ed8b54
fix(mobile): center floating status actions (#24526)
PixPMusic Oct 25, 2026
453bf01
fix(mobile): the iPhone new-chat button sits beside the tab bar on iO…
wojas Oct 25, 2026
6548c00
fix(web): signed-out T3 Connect shows a Sign in button (#24636)
t3dotgg Oct 25, 2026
62eb35b
perf(mobile): stop menu fault logging and whole-list re-renders on Ho…
juliusmarminge Oct 25, 2026
074d9e4
fix(mobile): preserve syntax and navigation in large diffs (#24276)
juliusmarminge Oct 25, 2026
67c3afb
perf(shell): cold connects skip idle subagent threads, a third of the…
t3dotgg Oct 25, 2026
fb76594
feat: share a thread with a guest who watches live and sends messages…
t3dotgg Oct 25, 2026
2887855
fix(antigravity): say when sign-in cannot reach Google (#24652)
t3dotgg Oct 25, 2026
eabcfa2
fix: canonicalize git.cafe pull request links (#22634)
maria-rcks Oct 25, 2026
edb2b2f
feat(mobile): manage labels, edit PRs and comments, react, and refres…
juliusmarminge Oct 25, 2026
d228f1f
perf(web): highlight code with @pierre/highlights instead of Shiki (#…
maria-rcks Oct 25, 2026
e19fe08
perf(server): thread opens and live events send each message body onc…
t3dotgg Oct 25, 2026
8f26ed7
refactor: halve code comments and cap comment blocks at four lines (#…
maria-rcks Oct 25, 2026
68d5b7a
chore: fit three comments to the four-line cap so lint passes on main…
t3dotgg Oct 25, 2026
24c80f3
fix(claude): ENABLE_TOOL_SEARCH=auto no longer costs 10x cache writes…
t3dotgg Oct 25, 2026
8045537
perf(orchestration): thread snapshots send each long ID once (#24643)
t3dotgg Oct 25, 2026
e666c01
fix(mobile): keep visible diff files highlighted (#24682)
juliusmarminge Oct 25, 2026
8fdaf34
fix(mobile): Settings split view keeps its selection and its header m…
juliusmarminge Oct 25, 2026
90c455f
fix(mobile): every Duo header blurs under the vertical bar, from one …
juliusmarminge Oct 25, 2026
b808f7f
feat(settings): follow-ups steer the running turn by default (#24718)
maria-rcks Oct 25, 2026
a0e0f8a
fix(mobile): Duo threads no longer open shifted left (#24732)
juliusmarminge Oct 25, 2026
a7f7a87
chore(sync): merge upstream main and reset mobile to upstream
pseudoseed Oct 25, 2026
1d61cc7
refactor(client-runtime): use upstream voice input now that mobile di…
pseudoseed Oct 25, 2026
954dce4
fix(push): start the direct push sender when the server starts
pseudoseed Oct 25, 2026
8fca81d
fix(mobile): iPad sheets scroll to the end and keep their buttons on …
pseudoseed Oct 25, 2026
ef8c244
feat(mobile): fork builds carry the PseudoCode name, icon, and identity
pseudoseed Oct 25, 2026
f1cc2b6
feat(push): report Apple's last delivery result in push status
pseudoseed Oct 25, 2026
d0afa06
fix(mobile): keep iOS connected across resumes and log what the phone…
pseudoseed Oct 25, 2026
63e0148
feat(mobile): attach the terminal's last 200 lines to the chat in one…
pseudoseed Oct 25, 2026
a9fe8fb
feat(mobile): deliver agent alerts through Apple push from your own s…
pseudoseed Oct 25, 2026
9db0f06
feat(mobile): home screen widgets and Live Activity follow PseudoCode…
pseudoseed Oct 25, 2026
5162ea2
feat(mobile): composer shows context window fill and thread cost
pseudoseed Oct 25, 2026
2e3a695
fix(mobile): fork builds skip upstream's over-the-air updates by default
pseudoseed Oct 25, 2026
2b8a79e
chore(mobile): drop the removed voice module from build scripts and docs
pseudoseed Oct 25, 2026
658daa4
fix(mobile): use upstream's Live Activity instead of the PseudoCode o…
pseudoseed Oct 25, 2026
c5e9b86
fix(push): direct Live Activity pushes carry only AgentActivity's fields
pseudoseed Oct 25, 2026
a0385f9
fix(mobile): the iPad sidebar keeps the PseudoCode title on one line
pseudoseed Oct 25, 2026
5f1f320
fix(terminal): reopened mobile terminals keep output printed at the p…
pseudoseed Oct 25, 2026
7670c58
chore(server): shorten the shell integration header comment
pseudoseed Oct 25, 2026
452688e
chore: drop exports left unused by the mobile reset
pseudoseed Oct 25, 2026
bd69335
fix(mobile): the agent widget sync compiles under the React Compiler
pseudoseed Oct 25, 2026
1ae8a9c
chore(release): prepare PseudoCode 0.0.74
pseudoseed Oct 25, 2026
587cb12
fix(ship): release scripts use Node 26, the version the repo pins
pseudoseed Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 4 additions & 5 deletions .agents/skills/ship-pseudocode/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ One command each. Both run from anywhere in the repo.
.agents/skills/ship-pseudocode/scripts/ship-ios.sh # TestFlight upload
```

The scripts handle the parts that are easy to get wrong: they pick Node 24, put `/usr/bin` ahead of
Homebrew, export the branding variables the desktop build needs, bump the iOS build number, build
the voice artifacts that are not in git, pass the signing team to every target, and verify the
The scripts handle the parts that are easy to get wrong: they pick Node 26, put `/usr/bin` ahead of
Homebrew, export the branding variables the desktop build needs, bump the iOS build number, pass
the signing team to every target, and verify the
result before claiming success.

`ship-mac.sh` ends with `spctl` reporting `source=Notarized Developer ID`, then publishes the DMG to
Expand Down Expand Up @@ -121,7 +121,7 @@ been done.

| Symptom | Cause |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ERR_UNKNOWN_FILE_EXTENSION ".ts"` | Node 20 on PATH. The ship scripts pick Node 24; a hand-run command must too. |
| `ERR_UNKNOWN_FILE_EXTENSION ".ts"` | Node 20 on PATH. The ship scripts pick Node 26; a hand-run command must too. |
| `command not found: vp` | `node_modules/.bin` missing from PATH. |
| App bundle named `T3 Code.app` | `T3CODE_APP_NAME` not exported for the desktop build. |
| `skipped macOS notarization` | No notarytool keychain profile named `pseudocode`. `ROTATION.md` step 6. |
Expand All @@ -131,7 +131,6 @@ been done.
| App Store Connect rejects the upload | Build number already used. `ship-ios.sh` raises it; a hand-run archive does not. |
| `requires a development team` on an extension | Expo writes `appleTeamId` onto the app target only. `ship-ios.sh` passes `DEVELOPMENT_TEAM` to every target; a hand-run archive must too. |
| duplicate `.xcframework-ios.signature` | A binary target inside a pod's SwiftPM dependency. Its signature is collected twice and both copy into one Signatures folder. Pin below the release that added it. |
| missing `llama.xcframework` or bundled model | The voice artifacts are gitignored. `ship-ios.sh` builds them; a hand-run archive must run the two scripts in `apps/mobile/modules/t3-voice/scripts/`. |
| macOS refuses to open the DMG elsewhere | Signed but not notarized. |
| `Failed to create release, "workflow" scope` | `gh` resolved to the `upstream` remote, where this account has read only. The script passes `--repo` derived from `origin`. |
| Release tag and DMG filename disagree | The version bump missed `apps/server/package.json`. |
Expand Down
13 changes: 7 additions & 6 deletions .agents/skills/ship-pseudocode/scripts/_common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ repo_root() {
}

# /usr/bin first: Xcode's export step passes rsync flags only Apple's rsync accepts.
# Node 24: the build scripts are TypeScript that Node strips types from directly.
# Node 26, the version package.json pins: the build scripts are TypeScript that Node strips types from.
setup_path() {
local root="$1" node24
node24="$(ls -d "$HOME"/.nvm/versions/node/v24.* 2>/dev/null | sort -V | tail -1)"
if [ -z "$node24" ]; then
echo "Node 24 is required. Install it with: nvm install 24" >&2
local root="$1" node26
node26="$(ls -d "$HOME"/.nvm/versions/node/v26.* 2>/dev/null | sort -V | tail -1 || true)"
[ -n "$node26" ] || node26="$(brew --prefix node@26 2>/dev/null || true)"
if [ -z "$node26" ] || [ ! -x "$node26/bin/node" ]; then
echo "Node 26 is required. Install it with: brew install node@26" >&2
exit 1
fi
export PATH="/usr/bin:$node24/bin:$root/node_modules/.bin:$PATH"
export PATH="/usr/bin:$node26/bin:$root/node_modules/.bin:$PATH"
}

# Branding and identifiers live in .env.local. The desktop build reads T3CODE_APP_NAME and
Expand Down
8 changes: 0 additions & 8 deletions .agents/skills/ship-pseudocode/scripts/ship-ios.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ else
fi
export T3CODE_IOS_BUILD_NUMBER="$NEXT"

# On-device dictation links a llama.cpp xcframework and ships a speech model
# inside the app. Both are built or fetched by these scripts and neither is in
# git, so a fresh clone has an app that will not link. Both no-op when the
# artifact is already present at the pinned version.
echo "Preparing voice artifacts"
bash apps/mobile/modules/t3-voice/scripts/fetch-bundled-model.sh
bash apps/mobile/modules/t3-voice/scripts/build-llama-xcframework.sh

APP="$T3CODE_APP_NAME"
cd apps/mobile
mkdir -p build
Expand Down
36 changes: 7 additions & 29 deletions .github/scripts/ci-changes.cjs
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
// Decides which CI jobs a change needs. Prints `key=value` lines for
// $GITHUB_OUTPUT; a job runs unless its key is "false".
//
// node ci-changes.cjs --paths <file> gate by the listed paths
// node ci-changes.cjs --paths <file> --full run every Linux job, gate only the macOS lint
// node ci-changes.cjs changed paths unknown: run everything
//
// scripts/check-changed.ts requires the same decisions so local runs match CI.
//
// Every changed path maps to workspace packages, which then pull in their
// dependents from package.json. A path this file does not recognize runs
// everything, so a new root file or directory can only cost minutes.
// Unknown paths run every job; recognized packages pull in their dependents.
// scripts/check-changed.ts shares these decisions so local checks match CI.
const fs = require("node:fs");
const path = require("node:path");

const root = path.resolve(__dirname, "../..");

// Never read by a build, typecheck, or test. Lint still formats them.
const INERT = [
/^docs\//,
/^\.(agents|cursor|vscode|devcontainer|macroscope|repos)\//,
/^[^/]+\.md$/,
/^LICENSE$/,
];

// Run everything: the root vite.config.ts loads the first as setup for every
// package's tests, and the vendored mobile tarballs are installed dependencies.
// Shared test setup and vendored mobile dependencies affect every package.
const RUNS_EVERYTHING = [
/^packages\/shared\/src\/(HostProcess|testing\/longTempDir)\.ts$/,
/^apps\/mobile\/deps\//,
];

// Paths outside a package, or files inside one that other packages read
// through relative imports rather than a package.json dependency.
// Includes cross-package reads absent from package.json dependencies.
const EXTRA_OWNERS = [
// Vite configs and tsconfigs of these apps import scripts/lib directly.
[/^scripts\/lib\//, ["@t3tools/desktop", "@t3tools/mobile", "@t3tools/web", "t3"]],
Expand All @@ -43,9 +30,7 @@ const EXTRA_OWNERS = [
];
const RUST = /^native\/(resource-monitor|kde-snap-shot|hyprland-snap-shot)\//;

// Tests that read another package's files without declaring it. The scripts
// tests read app manifests, assets, and sources (cursor-sdk-packaging,
// third-party-licenses, brand-assets, build-desktop-artifact).
// These tests read other packages without declaring dependencies.
const TEST_ONLY_DEPS = {
"@t3tools/provider-pi": ["@t3tools/client-runtime"],
"@t3tools/scripts": [
Expand All @@ -56,17 +41,12 @@ const TEST_ONLY_DEPS = {
"@t3tools/provider-cursor",
],
};
// Declared dependencies that only feed a build step: the server bundles the
// built web app (apps/server/vite.config.ts) but never imports it.
// The server bundles the web build without importing it.
const BUILD_ONLY_DEPS = { t3: ["@t3tools/web"] };

// Sources scripts/mobile-native-static-check.ts lints, plus the tool and rule
// configuration that decides how it lints them, plus the root package.json
// that defines the lint:mobile command.
const MOBILE_NATIVE =
/^apps\/mobile\/.*\.(swift|kt|kts)$|^apps\/mobile\/(\.swiftlint\.yml|detekt\.yml|\.editorconfig|Brewfile)$|^scripts\/mobile-native-static-check\.ts$|^package\.json$|^\.github\/workflows\/ci\.yml$|^\.github\/scripts\/ci-changes\.cjs$|^\.github\/actions\/ci-changes\//;

// Jobs that test every package except these, which have jobs of their own.
const OWN_TEST_JOB = new Set(["t3", "@t3tools/web", "@t3tools/monorepo"]);

function readWorkspace() {
Expand Down Expand Up @@ -108,8 +88,7 @@ function readWorkspace() {
return packages;
}

// Returns the packages a path touches, [] for an inert path, or null when the
// path is not understood and everything must run.
// [] means inert; null means unknown and requires every job.
function ownersOf(file, packages) {
if (INERT.some((pattern) => pattern.test(file))) return [];
// A manifest can change dependencies or versions other packages read.
Expand Down Expand Up @@ -148,7 +127,6 @@ function decide(files, packages, full) {
if (everything) {
affected = all;
} else {
// Add dependents until nothing changes.
let grew = true;
while (grew) {
grew = false;
Expand Down
13 changes: 3 additions & 10 deletions .github/scripts/mirror-play-apk.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
// Publishes closed-testing betas, or downloads the internal APK for a normal
// nightly release. Play generates and signs the APK from the
// uploaded bundle with the app signing key, so it carries the same signature
// as the Play Store install. Run by .github/workflows/mobile-android-apk.yml.
// Play signs generated APKs with the app signing key, matching Play Store installs.
const { execFileSync } = require("node:child_process");
const crypto = require("node:crypto");
const fs = require("node:fs");
Expand Down Expand Up @@ -50,10 +47,7 @@ async function play(token, method, url) {
return response;
}

// Highest version code the track serves, with its release name. Play names a
// release after the bundle's versionName unless the uploader sets one. This
// endpoint reads without opening an edit; an edit would invalidate any open
// EAS Submit edit made with the same service account.
// Opening an edit would invalidate any EAS Submit edit using the same service account.
async function newestTrackBuild(token, appUrl, trackName) {
const { releases = [] } = await (
await play(token, "GET", `${appUrl}/tracks/${trackName}/releases`)
Expand Down Expand Up @@ -82,8 +76,7 @@ function readRelease(tag) {
}
}

// Download to a temporary directory so a failed request cannot leave a partial
// APK among the release assets.
// Stage downloads so failed requests cannot leave partial release assets.
async function downloadApk({ token, appUrl, build, apkName }) {
const generated = await (
await play(token, "GET", `${appUrl}/generatedApks/${build.versionCode}`)
Expand Down
7 changes: 2 additions & 5 deletions .github/scripts/stage-preview-bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
"server/dist/client/index.html",
"desktop/dist-electron/main.cjs",
}
# The current bundle is about 32 MiB compressed. Bound extraction on the
# trusted runner even when the PR replaces the uploader entirely.
# Bound trusted-runner extraction even when a PR replaces the uploader.
MAX_ARCHIVE_BYTES = 512 * 1024 * 1024
MAX_EXPANDED_BYTES = 2 * 1024 * 1024 * 1024
MAX_ENTRIES = 50_000
Expand Down Expand Up @@ -58,9 +57,7 @@ def stage_bundle(archive: Path, destination: Path):
files.add(name)
if not REQUIRED_FILES <= files:
raise ValueError("Preview bundle is missing required entry points")
# Validate all names before writing anything. This is a fresh directory
# outside the checkout; neither pre-existing links nor trusted files
# can be followed or overwritten. ZIP permissions are never restored.
# Validate before writing into a fresh directory so existing links/files cannot be followed or overwritten.
destination.mkdir(parents=True, exist_ok=False)
for entry in entries:
target = destination / entry.filename
Expand Down
3 changes: 1 addition & 2 deletions .github/scripts/thread-transfer-report.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,7 @@ async function resolve({ github, context, core }) {

const sourceArtifacts = await artifactsForRun(github, owner, repo, source.id);
const sourceArtifact = findResultArtifact(sourceArtifacts);
// CI requires the artifact whenever the server tests run, so a passing run
// without one skipped them because the PR cannot affect the server.
// Successful server tests require this artifact; its absence means CI skipped them.
if (!sourceArtifact && source.conclusion === "success") {
core.info("CI skipped the server tests for this change; nothing to report.");
core.setOutput("publish", "false");
Expand Down
Loading
Loading