Skip to content

feat: allow disabling AI-generated thread titles - #3

Merged
nullStack65 merged 2 commits into
mainfrom
feat/disable-ai-generated-thread-titles
Sep 23, 2026
Merged

nullStack65 merged 2 commits into
mainfrom
feat/disable-ai-generated-thread-titles

Conversation

@nullStack65

Copy link
Copy Markdown
Owner

Fork-only customization

This PR carries the T3 customization to let users disable AI-generated thread titles.

This is intentionally a fork-only change for nullStack65/t3code. It is not an upstream submission.

Behavior

  • Adds environment-wide generateThreadTitles, default true.
  • When disabled, T3 makes zero title-generation/refinement/regeneration LLM calls.
  • Threads retain the deterministic first-prompt-derived title.
  • The Regenerate title action is hidden when disabled.
  • Global Restore default settings restores the toggle back to true.

Validation

The implementation and restore-default repair were validated at exact head:

b3bd0032f529995d90a99b99a9b7fbcd804edb05

Targeted results included:

  • ProviderCommandReactor tests: title generation ON/OFF, refinement OFF, manual regeneration OFF.
  • Settings contract/default coverage.
  • Settings search coverage.
  • Restore-default regression coverage.
  • Scoped contracts/web/server typechecks.
  • Scoped lint and formatting checks.

Provenance

This work was accidentally opened against upstream as pingdotgg/t3code#12980. The canonical review/home is this fork PR. The upstream PR should remain closed.

Copy link
Copy Markdown
Owner Author

RESULT — canonical fork-only handoff

Status: PASS

Canonical PR: #3

Final head: b3bd0032f529995d90a99b99a9b7fbcd804edb05

Fork base main: b5a0f810108d42ca8635b5a3d75a6e885bb3a254

Placement: fork-only. This customization is not intended for submission to pingdotgg/t3code.

Implementation: AI thread-title generation can be disabled with generateThreadTitles=false; first-turn generation, refinement, and manual regeneration are gated; deterministic first-prompt titles remain; the UI hides Regenerate title when disabled.

Restore-default repair: global Restore default settings now includes generateThreadTitles and restores it to true, with dedicated regression coverage.

Prior durable receipts: the implementation and repair were originally recorded on accidental upstream PR pingdotgg/t3code#12980 at the same final head. That PR is being closed and is no longer canonical.

Validation: targeted server/settings/web tests, restore regression tests, scoped typechecks, lint, and formatting were reported passing on this exact head.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: nullStack65/t3code/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 03e7ece8-4da7-4108-806f-5f8389231f39


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.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M labels Sep 22, 2026
Add an environment-wide generateThreadTitles server setting, default on.
When off, T3 makes no title-generation LLM call: the first-turn
generation, refinement, and manual regeneration paths all return early,
leaving the deterministic first-prompt title in place. Manual
regeneration still completes through the existing path so no spinner or
titleRegeneration state is left pending.

The setting is surfaced in Settings > General > Text generation, is
searchable, and hides the per-thread Regenerate title action when off.
Conversation inference and provider/agent rename behavior are untouched.
Global "Restore default settings" omitted generateThreadTitles, so
turning AI thread titles off and then restoring defaults reported
success while leaving the setting false. Its default is true.

Add it to the changed-setting labels, the changed-setting memo
dependencies, and the restore updateSettings patch, matching the other
server settings. Cover the hook with a focused regression test proving
the false -> true reset and the changed label.
@nullStack65
nullStack65 force-pushed the feat/disable-ai-generated-thread-titles branch from b3bd003 to baba583 Compare September 22, 2026 21:53
@github-actions github-actions Bot added size:XXL and removed size:M labels Sep 22, 2026
@nullStack65 nullStack65 reopened this Sep 22, 2026
@github-actions

Copy link
Copy Markdown

Thread transfer impact

⚠️ The latest CI run did not produce a thread transfer result for baba583.

This comment will update automatically after the next completed run.

@nullStack65

Copy link
Copy Markdown
Owner Author

RESULT — current-main refresh

Status: PASS

Canonical PR: fork PR #3

Prior head: b3bd0032f529995d90a99b99a9b7fbcd804edb05

Final head: baba583b7eac769da3727c372d54b26c4b85a5d3

Fork main: d7819c18813fa03b033cc1c9472c9acc0ffc0618

Upstream main observed: d7819c18813fa03b033cc1c9472c9acc0ffc0618

Merge base (head vs fork main): d7819c18813fa03b033cc1c9472c9acc0ffc0618 — 2 ahead / 0 behind.

Refresh / rebase

Fork main was a pure ancestor of upstream (b5a0f8101… → 31 upstream commits, 0 fork-only commits), so it was fast-forwarded to current upstream main (d7819c1881…). No fork-only commits were overwritten. Upstream was touched read-only for fetch/compare only.

  • Commits rebased: 2 — 13622f918 (feature) and baba583b7 (restore-default fix).
  • Pre-rebase base: c26119ada3 (33 upstream commits behind current main).
  • Conflicts encountered: 0. The feature was replayed cleanly onto d7819c1881.
  • How resolved: no conflict markers. The only upstream change inside a feature file was an unrelated single-line DialogPanel cleanup in SettingsPanels.tsx (~line 899); the feature hunks sit in useSettingsRestore (~623/649/792) and GeneralSettingsPanel (~3192), so git merged them without overlap. All feature hunks were reviewed against current-main structure (SettingsRow/serverScoped/settingKeys, ScopedSwitch, searchableSetting, serverEnvironment.configValueAtom) and match the newest patterns.
  • GitHub note: updating fork main left the PR's cached base.sha stale (it still diffed against b5a0f81, showing 165 files). PR feat: allow disabling AI-generated thread titles #3 was closed and reopened to force recomputation; it now reports base.sha = d7819c1881 and 8 changed files. The branch itself was not modified.

Feature proof

  • Setting default: ServerSettings.generateThreadTitles = Schema.Boolean with withDecodingDefault(true); ServerSettingsPatch.generateThreadTitles accepts an optional boolean. Users without the stored field decode as true (covered by tests).
  • Initial-title gating: maybeGenerateThreadTitleForFirstTurn reads projectSettingsForThread(...).generateThreadTitles and returns before any LLM call when off.
  • Refinement gating: maybeRefineThreadTitle returns before dispatching thread.title.refine when off. Because refinement's actual generation runs through regenerateThreadTitle (the refine command emits thread.meta-updated with regenerateTitle: true), the regeneration gate also covers it.
  • Manual-regenerate gating: regenerateThreadTitle returns { _tag: "Completed", title: undefined } when off, so no LLM call and no stuck titleRegeneration/spinner.
  • Deterministic title preservation: with all generation paths early-returning, the thread keeps the deterministic first-prompt-derived title; no generation completes to overwrite it.
  • Restore-default behavior: useSettingsRestore includes generateThreadTitles in changed-setting labels, in the memo dependency list, and in the updateSettings restore patch (DEFAULT_UNIFIED_SETTINGS.generateThreadTitles, i.e. true). The dedicated regression test proves false → reported changed → reset to true, and that default true is not reported.
  • Action-menu behavior: useThreadActionMenu ANDs generateThreadTitles (from server settings, defaulting to true) with readEnvironmentSupportsTitleRegeneration, so the Regenerate title action is hidden when the setting is off.

LLM call-site audit (current main)

Production textGeneration.generateThreadTitle call sites (non-test):

  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:971 — initial/first-turn title; guarded by if (!generateThreadTitles) return; at line 968.
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:1071 — manual regeneration; guarded at lines 1066–1070. This handler also executes refinement, so refinement is covered.
  • apps/server/src/textGeneration/TextGeneration.ts:165 — linked-context wrapper that re-invokes the service; reached only from the two gated reactor call sites.
  • apps/server/scripts/evaluate-thread-titles.ts:101 — offline dev/eval CLI, not a runtime T3 title path; intentionally not gated.

Refinement dispatch is separately guarded at ProviderCommandReactor.ts:1025–1026. Therefore OFF ⇒ zero T3-owned title inference (initial, refinement, manual regeneration). Conversation inference is untouched, and provider/agent explicit rename (thread.meta.update) never calls generateThreadTitle. No new title-generation call site was introduced by current main.

Tests (exact commands on final head)

  • packages/contracts: pnpm test src/settings.test.ts1 file, 145 passed.
  • apps/server: pnpm test src/orchestration/Layers/ProviderCommandReactor.test.ts1 file, 73 passed.
  • apps/web: pnpm test src/components/settings/SettingsPanels.restore.test.tsx src/components/settings/settingsSearch.test.ts2 files, 60 passed.
  • apps/web (directly relevant extras): pnpm test src/components/settings src/hooks/useThreadActions.test.ts src/hooks/useThreadActions.undo.test.ts48 files, 469 passed.
  • Typechecks: packages/contracts exit 0; apps/server 0 errors; apps/web 0 errors (only effect-tsgo suggestions).
  • Lint (touched files): exit 0 (warnings only, pre-existing patterns).
  • Format: vp fmt --check on the 8 touched files → "All matched files use the correct format."
  • git diff --check (range and worktree) → clean.

Note: this environment's TMPDIR is volatile and was being cleaned mid-run, which deadlocked the vitest fork workers (idle event loop, no progress). Tests were rerun with a stable TMPDIR and passed. No genuine test failures were observed.

Diff scope

Changed files (8), all feature + tests:

  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • apps/web/src/components/settings/SettingsPanels.tsx
  • apps/web/src/components/settings/SettingsPanels.restore.test.tsx
  • apps/web/src/components/settings/settingsSearch.ts
  • apps/web/src/hooks/useThreadActionMenu.ts
  • packages/contracts/src/settings.ts
  • packages/contracts/src/settings.test.ts

git diff upstream/main...HEAD is exactly these 8 files (+433 / −9). No synchronization/upstream noise is in the PR diff; the 31 upstream sync commits live on fork main, not in this PR.

PR state

  • Base/head: main (d7819c1881) ← feat/disable-ai-generated-thread-titles (baba583b7).
  • Mergeability: MERGEABLE; mergeable_state = unstable (CI running).
  • Checks: latest run is in progress. The only non-passing entry reported was Mobile Native Static Analysis from the pre-reopen run 35789290394, whose run conclusion is cancelled (superseded by the reopen), not a genuine failure; the PR touches no mobile files.
  • Reviews: none. Review comments / unresolved review threads: none.

Placement

Fork-only customization; pingdotgg/t3code is read-only upstream and no upstream PR is open. (The accidental pingdotgg/t3code#12980 remains closed.)

@nullStack65
nullStack65 merged commit bcc1a58 into main Sep 23, 2026
20 of 39 checks passed
@nullStack65

Copy link
Copy Markdown
Owner Author

RESULT — MERGED

Status: PASS

PR: fork PR #3#3

Accepted head: baba583b7eac769da3727c372d54b26c4b85a5d3 (exact, unchanged since last verification)

Pre-merge base: d7819c18813fa03b033cc1c9472c9acc0ffc0618 (fork main)

Merge commit: bcc1a58b19a9d610a4f08fed191a364767bc65b3

Resulting fork main: bcc1a58b19a9d610a4f08fed191a364767bc65b3

Acceptance

  • Mergeability: GitHub reported MERGEABLE immediately before the merge; main is unprotected; merged with the repository's normal method (squash), producing a single-parent commit on main.
  • Review-thread state: zero reviews, zero review comments, zero review threads — none unresolved, and none identifying a correctness issue.
  • CI status: the current-head CI run 35789484948 (head baba583) never started a single job — Check, Test, Test Server 1–3, Rust, Mobile Native Changes, Native fingerprint diff, and Release Smoke were still queued after ~4 hours because runners were unavailable. The only non-passing PR entry, Mobile Native Static Analysis, belongs to the earlier superseded run 35789290394, whose run conclusion is cancelled, not failed; the PR touches no mobile files. No real current-head failure exists.
  • Full CI completed or runner-queued: runner-queued / never started; no completed current-head CI result was available. Merge was made under the exact-head validation authorization for an indefinitely queued runner job.
  • Exact-head targeted validation relied upon: independently re-run here against a tree whose 8 changed-file blob SHAs are byte-identical to baba583 on the identical base d7819c1:
    • packages/contracts pnpm test src/settings.test.ts145 passed
    • apps/server pnpm test src/orchestration/Layers/ProviderCommandReactor.test.ts73 passed
    • apps/web SettingsPanels.restore.test.tsx2 passed; settingsSearch.test.ts58 passed (60 total)
    • packages/contracts pnpm typecheck → exit 0 (suggestions only)
    • Recorded additionally: apps/server/apps/web typechecks clean; touched-file lint clean apart from documented pre-existing warnings; formatting clean; git diff --check clean.
    • Note: vitest's default fork pool deadlocked in this environment; the web suites were completed with --pool=threads. This was an environment issue, not a test failure.

Feature landed

  • AI title toggle: ServerSettings.generateThreadTitles (boolean, decoding default true) plus the patch field; surfaced as a Settings > General control and a searchable entry.
  • Zero title-generation inference when disabled: first-turn generation returns early, the refinement dispatch returns early, and manual regeneration returns Completed/undefined before any generateThreadTitle call.
  • Deterministic title retention: with every generation path early-returning, the deterministic first-prompt title persists.
  • Restore-default repair: useSettingsRestore now includes generateThreadTitles in the changed-setting labels, the memo dependencies, and the restore patch (DEFAULT_UNIFIED_SETTINGS.generateThreadTitles === true), with a focused regression test.
  • Regenerate-action behavior: useThreadActionMenu ANDs generateThreadTitles with readEnvironmentSupportsTitleRegeneration, hiding Regenerate title when off; explicit provider/agent rename and conversation inference remain untouched.

Scope

  • Changed-file count: 8ProviderCommandReactor.ts, ProviderCommandReactor.test.ts, SettingsPanels.tsx, SettingsPanels.restore.test.tsx, settingsSearch.ts, useThreadActionMenu.ts, settings.ts, settings.test.ts.
  • compare d7819c1...bcc1a58 confirms exactly these 8 files with ahead_by = 1; no unrelated upstream-synchronization changes were mixed into the feature delta. All 8 blob SHAs at the merge commit equal the accepted-head blobs, so the accepted head is contained in fork main.

Ownership

Landed only in nullStack65/t3code. pingdotgg/t3code remains read-only upstream.

@nullStack65

nullStack65 commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

INSTALL RECEIPT — macOS

Status: PASS

Machine: macOS 26.6.2 (x86_64)

Architecture

  • Intel/x86_64 — Intel(R) Core(TM) i5-10600 CPU @ 3.30GHz

Previous installation

  • app path: /Applications/T3 Code (Alpha).app
  • version: 0.0.42
  • installation owner/method: Homebrew cask t3-code (auto_updates); Developer ID signed
  • upstream/fork determination: upstream official pingdotgg/t3code build — Developer ID "T3 Tools, Inc. (ARK85ZXQ4Z)", notarized, with app-update.yml pointing at owner pingdotgg, repo t3code (would have auto-updated back to upstream)

Fork source

  • checkout/worktree path: /Users/businessaccount/t3-fork-build (fresh single-branch clone; the older Documents checkout had corrupt packfiles and was left untouched)
  • remote: git@github.com:nullStack65/t3code.git
  • branch: main
  • exact source SHA: bcc1a58b19a9d610a4f08fed191a364767bc65b3 (PR feat: allow disabling AI-generated thread titles #3 squash-merge "feat: allow disabling AI-generated thread titles")

Artifact

  • packaging command: vp run dist:desktop:dmg:x64node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch x64
  • DMG path: /Users/businessaccount/t3-fork-build/release/T3-Code-0.0.42-x64.dmg
  • signing/Gatekeeper status: unsigned local build (code object is not signed at all). No quarantine attribute was applied to the locally built artifact; Gatekeeper was not weakened globally.

Installed fork

  • .app path: /Applications/T3 Code (Alpha).app
  • executable path: /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
  • proof running process is fork build: PID 87527 runs from the installed bundle; its server child 87594 runs app.asar/apps/server/dist/bin.mjs; installed app.asar embeds t3codeCommitHash = bcc1a58b19a9; executable is Mach-O x86_64; version 0.0.42. This agent session itself is now hosted by the fork app's server (opencode PID chain rooted at 87527 → 87594).

State preservation

  • settings: present — settings.json, client-settings.json, keybindings.json, desktop-settings.json intact (a backup copy is in ~/t3-state-backup/)
  • auth/providers: clerk-tokens.json present; secrets/ entries 7362 → 7367 (grew with normal use)
  • projects/history: projection_projects 5 → 5; projection_threads 370 → 370; live state.sqlite untouched by the app swap (read-only VACUUM INTO snapshot at ~/t3-state-backup/state.sqlite.snapshot)
  • connection/pairing state: auth_pairing_links 361 → 362; auth_sessions 228 → 230; server-runtime.json present and pointing at the running fork server
  • provider detection: providers observed in logs after relaunch — codex, codex-go-pool, codex-astra-pool, codex-openrouter-luna, claude-go-pool, opencode, opencode-go-pool. No re-authentication was required.

Update ownership

  • Homebrew/updater state: cask t3-code uninstalled (brew list --cask no longer lists it); the installed fork app has app-update.yml = absent and no publish feed
  • protections against reverting to upstream: the official cask was removed, and the fork build embeds no update feed (built without T3CODE_DESKTOP_UPDATE_REPOSITORY/GITHUB_REPOSITORY), so the Electron updater cannot pull an upstream pingdotgg build
  • exact future fork-update procedure:
    1. git -C /Users/businessaccount/t3-fork-build fetch origin main && git -C /Users/businessaccount/t3-fork-build checkout main && git -C /Users/businessaccount/t3-fork-build pull --ff-only
    2. build Intel DMG: vp run dist:desktop:dmg:x64
    3. quit: osascript -e 'quit app "T3 Code (Alpha)"'
    4. replace: rm -rf "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/Applications/T3 Code (Alpha).app" && ditto "<built app>" "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/Applications/T3 Code (Alpha).app"
    5. xattr -dr com.apple.quarantine "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/Applications/T3 Code (Alpha).app"
    6. launch: open -a "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/Applications/T3 Code (Alpha).app"
    7. verify the installed app.asar t3codeCommitHash equals the new fork SHA

Feature proof

  • setting visible: "Generate thread titles with AI" is present in the built app under Settings → General → Text generation, with description "When off, new threads use the first prompt as their title instead of asking a model to name them."
  • deterministic title smoke result: the fork's own tests assert the exact behavior — "keeps the deterministic first-prompt title when AI title generation is disabled" and "generates a first-turn title when the setting is left at its default" (ProviderCommandReactor.test.ts), plus "defaults AI thread title generation on" / "accepts disabling AI thread title generation" (settings.test.ts). Targeted run of ProviderCommandReactor.test.ts + settings.test.ts + SettingsPanels.restore.test.tsx = 220 passed. Server code early-returns title generation when the setting is false (ProviderCommandReactor.ts:968,1026,1068), so the first-prompt title is not replaced.

Issues / deviations

  • The initial detached installer was submitted with launchctl submit, which set keepalive and re-ran the script; it completed the install twice and posted two receipts. The job was removed, the duplicate receipt deleted, and this comment is the consolidated final receipt. The running app was verified after the loop stopped.
  • Local build is unsigned (no Apple Developer cert on this machine); Gatekeeper was not weakened globally.
  • Machine Command Line Tools are outdated (clang 12 / SDK 11.1); the native node-pty rebuild required Homebrew LLVM 20 with an explicit -isysroot to the CLT SDK.
  • The live UI toggle smoke (flipping the setting in the running app and creating a disposable thread) was not automated; the behavior is proven by the fork's targeted tests above.

Installed by the T3 Code machine migration agent; source bcc1a58b19a9.

@nullStack65

Copy link
Copy Markdown
Owner Author

INSTALL RECEIPT — Windows (START)

Scope: migrate the Windows T3 Code desktop install from the stock upstream build (pingdotgg/t3code, winget T3Tools.T3Code 0.0.40) to a locally built nullStack65/t3code/main artifact, preserving all user state and keeping update provenance on the fork.

Head: fork main @ bcc1a58b19a9d610a4f08fed191a364767bc65b3 — verified ancestor of PR #3; contains generateThreadTitles and the Generate thread titles with AI setting (apps/web/src/components/settings/SettingsPanels.tsx).

Recon (observed):

  • Installed: T3 Code (Alpha) 0.0.40, winget id T3Tools.T3Code, exe %LOCALAPPDATA%\Programs\t3code\T3 Code (Alpha).exe; resources/app-update.ymlpingdotgg/t3code (stock, upstream).
  • Installed app.asar does not contain generateThreadTitles → currently stock upstream.
  • Running: stock Electron processes + resources/resource-monitor/t3-resource-monitor.exe.
  • Windows x64 (AMD64), Win 10.0.26200.
  • No Windows Node/pnpm/vp/Rust/MSVC present — prerequisites must be installed for the build.
  • User state: %APPDATA%\t3code, %LOCALAPPDATA%\t3code-updater, C:\Users\nullstack65\.t3\userdata (secrets, wsl-server-tree); WSL Ubuntu-24.04 ~/.t3/wsl-runtime (0.0.40).
  • Local checkout Desktop\projects\t3code-omp-main-int (remotes fork=nullStack65/t3code, origin=pingdotgg/t3code); fork/main fetched = bcc1a58b1.

Plan: build x64 NSIS from fork main with vp run dist:desktop:win:x64; install over the winget install in place (same appId → preserves user data); pin T3Tools.T3Code in winget and build with no update feed so the app cannot silently return to upstream. Checkpoints follow.

@nullStack65

Copy link
Copy Markdown
Owner Author

INSTALL RECEIPT — Windows

Status: PASS (one documented deviation: no bundled WSL runtime archive)

Machine: Windows x64 (AMD64), Windows 10.0.26200, host DESKTOP-GM0G7BK.

Previous installation

  • Version: T3 Code (Alpha) 0.0.40
  • Executable: C:\Users\nullstack65\AppData\Local\Programs\t3code\T3 Code (Alpha).exe
  • Install owner/method: winget package T3Tools.T3Code; electron-builder NSIS per-user install under %LOCALAPPDATA%\Programs\t3code (uninstall key e9197887-efb3-55e0-985e-d6d3b5dd594a).
  • Upstream/fork determination: stock upstream. resources/app-update.yml pointed at owner: pingdotgg, repo: t3code; installed app.asar did not contain generateThreadTitles.

Fork source

  • Checkout path: C:\Users\nullstack65\Desktop\projects\t3code-fork-win (git worktree of the canonical checkout ...\projects\t3code-omp-main-int), clean/detached.
  • Remote: fork = https://github.com/nullStack65/t3code.git (upstream origin = pingdotgg, untouched).
  • Branch: fork/main (detached).
  • Exact source SHA: bcc1a58b19a9d610a4f08fed191a364767bc65b3 — verified ancestor of PR feat: allow disabling AI-generated thread titles #3; contains generateThreadTitles in apps/server/src/orchestration/Layers/ProviderCommandReactor.ts, packages/contracts/src/settings.ts, and apps/web/src/components/settings/SettingsPanels.tsx (Generate thread titles with AI). Fork main had not advanced past this commit.

Artifact

  • Architecture: x64
  • Packaging command: vp run dist:desktop:win:x64node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64 (with T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR=true; no GITHUB_REPOSITORY/T3CODE_DESKTOP_UPDATE_REPOSITORY set).
  • Artifact: C:\Users\nullstack65\Desktop\projects\t3code-fork-win\release\T3-Code-0.0.42-x64.exe (134,127,549 bytes, NSIS one-click)
  • SHA-256: E42F71279BFFA6F0E02E0F6900B761BA185CA97D675B2E205916FAB5B8134AFC
  • No latest.yml emitted (no update feed configured — intentional).

Installed fork

  • Executable: C:\Users\nullstack65\AppData\Local\Programs\t3code\T3 Code (Alpha).exe (246,220,288 bytes; ProductVersion 0.0.42.0)
  • Install mechanism: ran the fork NSIS installer in place over the winget install (same appId com.t3tools.t3code → same directory, upgraded the ARP entry to T3 Code (Alpha) 0.0.42).
  • Proof running app came from the fork: installed resources/app.asar contains generateThreadTitles; installed resources/server.asar contains generateThreadTitles and the UI label Generate thread titles with AI; app reports Version 0.0.42 in Settings → General; the single Start Menu shortcut resolves to the same replaced exe, so no stock executable can win a normal launch.

State preservation

  • Settings: preserved; settings.json retains all prior keys plus the fork's "generateThreadTitles": false. desktop-settings.json retains serverExposureMode: network-accessible and wslDistro: Ubuntu-24.04.
  • Auth/providers: preserved — clerk-tokens.json, secrets/ (incl. server-signing-key.bin, dPoP proofs), encrypted connection-catalog.json, state.sqlite (297 MB) all intact; Providers list unchanged (opencode enabled; cursor/grok disabled). Connections page still shows 3 authorized clients.
  • Projects/history: preserved — existing project groups and threads render in the sidebar (including this task's thread); no reset.
  • WSL: ~/.t3/wsl-runtime/sha256-02c38816… untouched. Desktop still detects the distro (WSL backend dropdown lists Ubuntu-24.04 (default) and docker-desktop). WSL backend was already Off; no WSL provider config was modified.
  • Pre-install safety backup: %LOCALAPPDATA%\t3-build\backup-preinstall\ (.t3\userdata 853 MB, %APPDATA%\t3code 204 MB).

Update ownership

  • Upstream auto-update neutralized: the fork build has no resources/app-update.yml and no latest*.yml, so electron-updater has no feed to poll — it cannot silently return to an official pingdotgg/t3code build.
  • Winget neutralized: winget pin add --id T3Tools.T3Code (pin type Pinning), so winget upgrade will not replace it. Installed winget version now reads 0.0.42 (== available), so there is nothing to upgrade anyway.
  • Future fork-update procedure (machine-specific):
    1. In ...\projects\t3code-omp-main-int: git fetch fork origin → update fork main per normal workflow (merge upstream into fork, push fork).
    2. In the worktree ...\projects\t3code-fork-win: check out the new fork/main, then
      • $env:Path="C:\Users\nullstack65\AppData\Local\t3-build\node-v24.21.0-win-x64;$env:Path"
      • $env:T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR="true" (or install Rust + VS Build Tools and drop the flag)
      • vp install then vp run dist:desktop:win:x64
    3. Install release\T3-Code-<version>-x64.exe (/S for silent). User data is untouched by the upgrade.
    • Do not set GITHUB_REPOSITORY/T3CODE_DESKTOP_UPDATE_REPOSITORY unless you want the app to poll a feed; to receive fork self-updates, first publish Windows release assets + latest.yml on nullStack65/t3code and build with T3CODE_DESKTOP_UPDATE_REPOSITORY=nullStack65/t3code.

Feature proof

  • Generate thread titles with AI is visible in the running app under Settings → General → Text generation (also surfaced by settings search). Screenshot: C:\Users\nullstack65\AppData\Local\t3-build\t3-title-setting-open.png.
  • Toggled OFF, then created a disposable thread with first prompt WINDOWS FORK TITLE TEST 2026.
  • Result: thread title = WINDOWS FORK TITLE TEST 2026 (exactly the first prompt; no AI-generated replacement). Screenshot: ...\t3-test-thread-sent.png. The provider (OpenCode/DeepSeek) responded to the turn, so provider smoke also passed. Disposable thread was settled out of the active list.

Issues / deviations

  • WSL runtime archive not bundled: the fork artifact was built without --wsl-runtime, so resources/wsl-runtime.tar.gz is absent (the stock build had it). Impact is currently nil because the WSL backend is Off; the existing WSL runtime and config are preserved. To restore WSL bundling, build the Linux CLI archive from the same SHA (scripts/build-cli-archive.ts) and rebuild with --wsl-runtime <t3-<ver>-linux-x64.tar.gz>.
  • resource-monitor reused, not rebuilt: t3-resource-monitor.exe was byte-copied from the stock 0.0.40 install into native/resource-monitor/target/x86_64-pc-windows-msvc/release/ and the build used T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR=true. It is an unchanged upstream helper, but it is the 0.0.40 binary rather than a 0.0.42 rebuild (Rust/MSVC are not installed on this machine).
  • Toolchain installed user-local: no system Node/pnpm/Rust/MSVC existed. Installed portable Node v24.21.0 at %LOCALAPPDATA%\t3-build\node-v24.21.0-win-x64 (not permanently on PATH) and vite-plus@0.3.3 into that prefix. No other global dev tooling was altered.
  • desktop-settings.json mainWindowBounds changed only because the window had been on a disconnected monitor and was moved on-screen for verification (cosmetic).
  • The disposable test thread was settled, not hard-deleted.
  • Update track remains Stable; "Check for Updates" reports up to date (no feed present).

@nullStack65

Copy link
Copy Markdown
Owner Author

START — Windows final verification / handoff

Scope: determine actual current Windows T3 install state, finish anything genuinely incomplete, post durable Windows INSTALL RECEIPT.

  • Owning destination: this PR (feat: allow disabling AI-generated thread titles #3), MERGED at bcc1a58b19a9d610a4f08fed191a364767bc65b3
  • Target source: nullStack65/t3code main @ bcc1a58
  • Running app observed: C:\Users\nullstack65\AppData\Local\Programs\t3code\T3 Code (Alpha).exe
  • Winget observed: T3Tools.T3Code 0.0.42 (needs ownership/provenance verification)
  • No reinstall performed yet; read-only recon in progress.

@nullStack65

Copy link
Copy Markdown
Owner Author

INSTALL RECEIPT — Windows

Status: PASS

The prior migration did finish on this machine. No reinstall was performed; this is verification + a durable receipt. I confirmed the running app is a fork build, confirmed state preservation, confirmed update ownership cannot silently revert to upstream, and confirmed the fork feature with source, bundle, persisted-settings, and live-UI evidence.

Machine

  • Windows 11 Home, version 10.0.26200 (build 26200)
  • Architecture: x64 / AMD64

Previous/current installation

  • Previous source: official upstream T3 Code (Winget package T3Tools.T3Code); updater cache shows it was on 0.0.400.0.42 before the fork install.
  • Installed app path: C:\Users\nullstack65\AppData\Local\Programs\t3code
  • Executable path: C:\Users\nullstack65\AppData\Local\Programs\t3code\T3 Code (Alpha).exe (running; main PID 9280, plus renderer/sidecar processes)
  • Version: 0.0.42 (file/product version 0.0.42.0); matches fork apps/desktop/package.json at bcc1a58.
  • Install method: per-user NSIS install (Uninstall T3 Code (Alpha).exe /currentuser), ARP key e9197887-efb3-55e0-985e-d6d3b5dd594a, publisher "T3 Tools". Built and installed from the fork (not via Winget).
  • Only one T3 installation exists on the machine (no separate official install in Program Files).

Fork provenance

  • Checkout path: C:\Users\nullstack65\Desktop\projects\t3code-fork-win
  • Remote: fork https://github.com/nullStack65/t3code.git (writable); origin https://github.com/pingdotgg/t3code.git (read-only upstream)
  • Branch/HEAD: detached HEAD at bcc1a58b19a9d610a4f08fed191a364767bc65b3
  • Exact source SHA: bcc1a58b19a9d610a4f08fed191a364767bc65b3 — verified identical to nullStack65/t3code refs/heads/main via git ls-remote.
  • Proof the running application came from that fork source:
    • Installed resources\app.asar and resources\server.asar both contain the identifier generateThreadTitles (app.asar: 4 hits, server.asar: 28 hits). Upstream origin/main contains 0 occurrences; the identifier was introduced only by merge bcc1a58 (7 files). This symbol does not exist in upstream, so the installed bundle cannot be an upstream build.
    • Build log %LOCALAPPDATA%\t3-build\build-win.log records the exact fork build command and artifact: node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64release\T3-Code-0.0.42-x64.exe (134,127,549 bytes), timestamped 03:37, matching the installed app's file times (03:37) and the cached t3code-updater\installer.exe (identical size).
    • Installed bundle hashes: app.asar SHA256 7A86856207544E07B403CC608E2CB42D558C145BD451C9B95E94F76256AC29D1; server.asar SHA256 22B39D55A4FFC338F35598C05C9B4ABF19E32F29C8CE9A98C5313D7E1F1BCADF.
    • Fork installer artifact SHA256: E42F71279BFFA6F0E02E0F6900B761BA185CA97D675B2E205916FAB5B8134AFC.
  • Embedded commit hash: none found in the artifacts (searched asar for bcc1a58… and nullStack65 → 0). Provenance is therefore via the feature marker + build log + version, not an embedded SHA.

State preservation

  • Settings: %USERPROFILE%\.t3\userdata\settings.json present (providers: opencode enabled, cursor/grok disabled; providerInstances.opencode binary configured). Also keybindings.json, client-settings.json, desktop-settings.json, themes\.
  • Providers/auth: userdata\secrets\ populated (incl. server-signing-key.bin, many dpop-proof-*.bin), clerk-tokens.json present. Provider config intact.
  • Projects/threads/history: userdata\state.sqlite ~300 MB live (locked by the running server), plus -wal/-shm; projects and thread history render in the running UI sidebar.
  • WSL: desktop-settings.jsonwslDistro: Ubuntu-24.04; userdata\wsl-server-tree\0.0.40 present. WSL integration retained.
  • Pairing/connections: connection-catalog.json present (encrypted catalog); serverExposureMode: network-accessible; server running at 127.0.0.1:3773 (PID 9592).
  • Nothing was reset.

Update ownership

  • Winget status: T3Tools.T3Code 0.0.42 is registered and matches the single installed ARP entry, but a blocking pin is already in placewinget pin listT3Tools.T3Code … Pin type: Pinning. winget upgrade offers nothing for T3. This neutralizes package-manager replacement of the fork.
  • Updater/feed status: no resources\app-update.yml and no latest*.yml in the installed resources → electron-updater has no feed. The repo's own build script documents this: builds made without a publish config "ship without app-update.yml … the build itself reports that no update feed is configured instead of polling."
  • Runtime proof it cannot silently revert: in desktop.trace.ndjson, the pre-fork official sessions (traceIds 3a0cd9bb, 77cf7a71, 760201fa) each started pollers and ran desktop.updates.checkForUpdates every ~4 min (422 checks, logging "update available"). The current fork session (traceId c2e9c385, started 04:58 local) has no startPollers and no checkForUpdates spans — only the one-time desktop.updates.configure. The fork build does not poll any upstream feed.
  • Background behavior: the only T3 scheduled task is T3 Repair Relaunch, whose action is simply launching the installed exe (no update/reinstall). No winget auto-update task.
  • Residual (inert): %LOCALAPPDATA%\t3code-updater\pending\T3-Code-0.0.40-x64.exe is a stale official installer from 09-14 left in the updater cache. It cannot be applied (no feed) and is older than the installed version, so it is a downgrade that will never be used. Left in place to avoid unnecessary deletion.
  • Exact future update procedure: in the fork checkout, node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64, then run the produced release\T3-Code-<ver>-x64.exe NSIS installer over the existing install (user data in %USERPROFILE%\.t3 is preserved). Keep the Winget blocking pin; do not run winget upgrade T3Tools.T3Code (if ever needed: winget pin remove --id T3Tools.T3Code, install the fork, then re-pin).

Feature proof

  • Setting visible: live UI screenshot of Settings → General → Text generation → "Generate thread titles with AI" (description: "When off, new threads use the first prompt as their title instead of asking a model to name them."), toggle present. Settings search for "thread titles" returns it. (Captured on this exact install, 2026-09-23.)
  • Live deterministic-title smoke (performed on this install by the prior migration step, 2026-09-23 05:05–05:08 local, with screenshots in %LOCALAPPDATA%\t3-build):
    1. Toggle set to OFF (t3-title-toggle-off.png).
    2. Disposable thread created with first prompt WINDOWS FORK TITLE TEST 2026 (t3-test-thread-typed.png, t3-test-thread-sent.png).
    3. Sidebar title remained exactly WINDOWS FORK TITLE TEST 2026 — deterministic from the first prompt, not AI-renamed.
    • Persisted state confirms the toggle: settings.json"generateThreadTitles": false. The live state.sqlite contains the prompt/title string and the setting key.
  • Source/test basis: generateThreadTitles is defined in packages/contracts/src/settings.ts and consumed in apps/server/src/orchestration/Layers/ProviderCommandReactor.ts and apps/web/src/components/settings/SettingsPanels.tsx at bcc1a58; upstream has none of these.

Issues / deviations

  • generateThreadTitles is currently false (the smoke left the toggle OFF). This is a user-facing setting, not a migration defect; it can be re-enabled in the UI. I did not reset it.
  • No embedded commit SHA in the shipped artifacts; fork provenance is established indirectly (unique feature marker in the installed asar + matching build log/version + fork-main SHA).
  • Winget ARP entry is still registered under the official package id T3Tools.T3Code; this is a name/GUID match only and is neutralized by the blocking pin.
  • A stale official 0.0.40 installer remains in the updater cache (inert; documented above).

Copy link
Copy Markdown
Owner Author

COORDINATION — fork releases, new-machine installation, and WSL backend

Owner request: support the Windows WSL backend and establish proper, repeatable builds/releases/install/update procedures for this fork. Desktop migration alone is not distribution completion.

Refreshed baseline

Work ownership / discovery

R — release implementation owner: sole repository code writer for this wave; create a narrowly scoped release/distribution PR into nullStack65/t3code/main and link it here immediately. Own workflows, packaging/provenance/versioning, update-source isolation, installer guidance, and integration of native-agent findings.

W — Windows/WSL qualification owner: on the actual Windows machine; no competing source edits. Rebuild native helper and matching Linux runtime, exercise packaged Windows-to-WSL startup in isolated state, correct WinGet pinning, and post machine/toolchain/artifact/test evidence here and on R's PR once available. Send source defects/patch proposals to R.

M — macOS qualification owner: on the actual Intel Mac; no competing source edits. Qualify reproducible DMG packaging, clean-profile downloaded-artifact launch, signing/notarization/update limitations, and state-preserving replacement/rollback. Post evidence here and on R's PR once available.

All agents refresh state and read this comment before work. Do not busy-wait for another agent; complete independent work, state remaining dependencies, and return. Baseline qualification is NOT final release-candidate acceptance.

Product contract

  • Required initial artifacts: Windows x64 installer WITH matching Linux x64 WSL runtime; Intel macOS desktop installer; separately downloadable matching Linux x64 runtime. Apple Silicon support should use existing targets when authorized native build/test capacity exists; untested platforms must be explicit rather than faked.
  • A release uses one exact source SHA/version across desktop, server, WSL payload, and rebuilt native helpers. Embed provenance and publish SHA-256 checksums for all final distributed artifacts. Do not reuse binaries from an installed upstream app.
  • Missing/wrong-source/wrong-architecture/corrupt WSL payloads must prevent a release-quality Windows package from being accepted. Test cold startup without an old T3 runtime cache or ambient Node, plus update, restart, and failure recovery.
  • New-machine consumers download an installer, not a compiler toolchain. Document WSL/OS/provider prerequisites separately.
  • Fork-only update source for desktop AND bundled CLI/WSL self-update surfaces. No fallback to pingdotgg releases. Existing no-feed installations require an explicit first upgrade, not magical self-migration.
  • Use a tested fork version/channel scheme; cover same-upstream-version rebuild ordering, future upstream increments, architecture selection, and preview exclusion. Do not assume SemVer build metadata sorts as an update.
  • Preserve current settings, auth, pairing, databases, projects, title preference, and active work. WSL support does not authorize silently migrating live sessions or leaving the user's backend selection changed.
  • Owner-controlled candidate build -> native acceptance -> reviewed publication/promotion. No scheduled public release, unattended upstream merge, public npm publish, or deployment of relay/mobile/Closura infrastructure.
  • Use authorized isolated/self-hosted build capacity only. No paid runner fallback, no registration of personal desktops as public-PR execution workers. Report missing runner/signing authorization explicitly; do not call queued CI a passed release gate.
  • Signed/notarized and manual unsigned installation are different support levels. Do not enable unsupported macOS auto-update or disable security verification to make tests pass. No new signing-account purchase or credential provisioning in this wave.

Completion gate

Source PR, successful real build receipts, complete candidate asset set, fork provenance/update checks, Windows native+WSL smoke, macOS clean-install smoke, upgrade/state/rollback checks, and an actual verified published release are separate gates. Do not report the whole setup DONE after only writing YAML or building locally.

R may implement/commit/push/open a fork PR and prepare draft candidate artifacts. Stable publication, new trust roots/runner services, and replacement of the live installed apps remain separate explicit gates. Results belong on GitHub: exact base/head, commands, artifact hashes, executed vs deferred tests, unresolved decisions, and the next executable action. Chat responses should return only status, PR/receipt URLs, and exact SHAs.

@nullStack65

Copy link
Copy Markdown
Owner Author

START — R, fork release implementation owner

Scope: fork-specific build/release entry point, release-quality artifact checks, fork identity/update isolation, signing honesty, new-machine install/maintenance docs. Base nullStack65/t3code:main @ bcc1a58b19a9d610a4f08fed191a364767bc65b3. Implementation PR into main will be linked here immediately on push.

Refreshed state: fork main = bcc1a58b1 (unchanged). Open fork PRs: #4 (metrics, unrelated), #2 (a3s strict-resume, unrelated), #1 (acp, unrelated). Releases: only a4-netcup-v0.0.42-f014905 (explicitly not ours). Repo self-hosted runners: none. GitHub-hosted standard runners available (public repo). release.yml is unusable for the fork (Blacksmith-only labels, production relay/Clerk/Cloudflare/Vercel/npm/AUR/Discord).

DESIGN / DECISIONS

Runner plan. Thin fork-release.yml reuses the existing release-desktop.yml workflow_call unchanged. Build jobs run on GitHub-hosted standard labels only (ubuntu-24.04, windows-2025, macos-15-intel) — free for public repos, no paid -large/-xlarge, no guessed labels, no self-hosted registration. Intel macOS capacity on macos-15-intel must be confirmed by one real dispatch; if unavailable, the operator requirement is stated and the source stays valid. Build/qualify jobs are contents: read; the single publication job is contents: write and gated behind an explicit publish input + environment. No PR code ever runs on a signing runner.

Versioning. Fork releases use a fork-owned, strictly increasing plain X.Y.Z line. nextForkReleaseVersion takes the upstream base and every existing fork release and returns bumpPatch(max(upstreamBase, existing)); validateForkReleaseVersion rejects any version that is not plain X.Y.Z, that is ≤ an existing fork release, or that is below the upstream base. This gives two fork releases on one upstream version (0.0.43, 0.0.44), always sorts above the upstream base (no downgrade, no upstream fallback), excludes preview/nightly identifiers, and never relies on SemVer build metadata. Today's local 0.0.42 installs upgrade to the first fork release 0.0.43.

App identity. appId stays com.t3tools.t3code; user-data paths, credential storage, and product name are unchanged so existing installs keep settings/auth/pairings/databases. Fork provenance is made visible by embedding repository, full source SHA, version, and arch in the packaged output (desktop staged package.json + a readable t3code-build-info.json; CLI archive gets t3code-build-info.json), not by a rebrand.

Update policy. packages/shared/src/cliRelease.ts currently hardcodes pingdotgg/t3code for both the download base URL and the release-index API; T3CODE_RELEASE_BASE_URL only redirected downloads, so t3 update still discovered upstream versions. Decision: default the release repository to nullStack65/t3code and add T3CODE_RELEASE_REPOSITORY override, closing the index gap for t3 update, pinned/managed runtime, SSH tunnel runtime, and the install scripts. Desktop app-update.yml is already derived from T3CODE_DESKTOP_UPDATE_REPOSITORY/GITHUB_REPOSITORY; the fork workflow sets it to nullStack65/t3code explicitly. Existing no-feed installs need one explicit first install of the release-managed build (documented), not silent self-migration.

Signing. Honest three-level model: unsigned manual-install artifacts always build; signed/notarized artifacts only when Apple/Azure secrets exist (reusing the existing auto-detect path); in-app desktop updates only where a feed is published and the platform supports it. macOS Intel auto-update is not claimed. No signing credentials are provisioned or purchased in this wave.

Gates remaining (separate, none claimed done). Source PR; real workflow build receipts; complete candidate asset set with WSL archive + checksums; Windows native+WSL smoke; macOS clean-install smoke; upgrade/state/rollback checks; and an actual verified published release. Publication, new trust roots/runner services, and replacing live installs remain owner-controlled and out of scope here.

Continuing implementation now.

@nullStack65

Copy link
Copy Markdown
Owner Author

RESULT — WINDOWS WSL QUALIFICATION

Status: PASS (baseline qualification, independent of R's release PR). Final-candidate acceptance against R's published installer is still required and is listed below. No competing source edits were made; nullStack65/t3code main is untouched.

Source SHA/version: bcc1a58b19a9d610a4f08fed191a364767bc65b3 (fork main, verified via git ls-remote). Version 0.0.42. Upstream pingdotgg/t3code read-only (main observed at f5ef0ddb90a8c36584e181b1913e7b8a5df30ffc).

R's implementation PR: not yet posted. Rechecked the fork PR list: only #1, #2, #3 (merged), #4 (metrics). No release/distribution PR exists to comment on. This receipt will be mirrored to it when it appears.


Toolchain and prerequisite changes (machine-local, no reboot, no security weakening)

  • WSL Ubuntu-24.04 (isolated build host, 12 vCPU, 924 GB free): installed Node 26.8.2 (user-local ~/w-t3/node26), vite-plus@0.3.3, rustup stable (cargo 1.96.0), gcc-mingw-w64-x86-64, plus libsecret-1-dev/pkg-config for the desktop JS bundle. Used existing passwordless sudo for apt only.
  • Windows: reused the existing portable Node v24.21.0 + vite-plus@0.3.3 at %LOCALAPPDATA%\t3-build. No MSVC/VS Build Tools installed and no system-wide tool upgrades.
  • Nothing on the live install was changed; the built installer was not installed.

Actual build commands

WSL (Linux x64 runtime, same SHA):

vp install
vp run --filter t3 build
cargo build --locked --release --manifest-path native/resource-monitor/Cargo.toml
node apps/server/scripts/cli.ts build-exe --verbose
node scripts/build-cli-archive.ts --platform linux --arch x64 --version 0.0.42 \
  --resource-monitor-dir ~/w-t3/cli-resource-monitor --output-dir release-cli
node scripts/smoke-cli-archive.ts --archive release-cli/*.tar.gz --expect-version 0.0.42

Windows cross-compile (helper only) and desktop packaging:

rustup target add x86_64-pc-windows-gnu
cargo build --locked --release --target x86_64-pc-windows-gnu --manifest-path native/resource-monitor/Cargo.toml
vp install
# T3CODE_DESKTOP_REUSE_RESOURCE_MONITOR=true; GITHUB_REPOSITORY and
# T3CODE_DESKTOP_UPDATE_REPOSITORY explicitly unset
node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64 \
  --wsl-runtime <t3-0.0.42-linux-x64.tar.gz> --verbose

Native helper provenance (built from source — nothing copied from the installed app)

  • Linux t3-resource-monitor: built at bcc1a58, ELF 64-bit x86-64, sha256 caf405d0493a1102fac3e46472857f407e7060a831469ba25b605f8dd53d5088.
  • Windows t3-resource-monitor.exe: built at bcc1a58 with x86_64-pc-windows-gnu, PE32+ x86-64, 544,256 bytes, sha256 cae8e1899e21edf8aca79f09d71b9847f7f7b74b7f86a5573b8655a90228addf. Executed on Windows → hello handshake: {"type":"hello","sidecarVersion":"0.1.0","platform":"windows","arch":"x86_64","capabilities":{...}}.
    • Deviation: this local build used mingw, not MSVC (no MSVC on this machine). Same source SHA and it runs, but R's release job must produce the MSVC-targeted binary.

Artifacts and hashes

Artifact Size (bytes) SHA-256
Linux x64 WSL runtime t3-0.0.42-linux-x64.tar.gz 64,102,061 cec2d40d3586cfb978b98e5842320f003893e828fda27f73b370aea50938e715
Windows installer T3-Code-0.0.42-x64.exe 197,849,066 fccb7bf47817e39a69a2ac95a10f0384d2b5e032974b6991311fa1f966323926
  • Linux archive smoke test passed: t3 --version and a real t3 serve answering HTTP 200 with an empty PATH (no ambient Node), inside the distro.
  • Installer extracted (48 files, matches the build's own Validated Windows payload (48 files, 16 sidecar natives) gate). Packaged:
    • resources/wsl-runtime.tar.gz = 64,102,061 bytes, sha256 cec2d40d… (byte-identical to the WSL-built archive);
    • resources/wsl-runtime.tar.gz.sha256 sidecar content cec2d40d…;
    • resources/resource-monitor/t3-resource-monitor.exe = 544,256 bytes, sha256 cae8e189… (the from-source helper);
    • resources/server.asar, resources/app.asar, win native packages (node-pty, @napi-rs/keyring, @clerk/electron-passkeys, @yuuang/ffi-rs, @ff-labs/fff-bin-win32-x64).
    • No app-update.yml, no latest*.yml → no Electron update feed is embedded.

WSL acceptance (executed; isolated HOME, no ambient Node)

Each install scenario drove the app's own install/prune/invalidate contract (buildWslRuntimeInstallScript / buildWslRuntimePruneScript / buildWslRuntimeInvalidateScript reproduced verbatim from apps/desktop/src/wsl/DesktopWslEnvironment.ts at bcc1a58) against the packaged archive.

  1. Cold install (PATH=/usr/bin:/bin, no node): extracted to ~/.t3/wsl-runtime/sha256-<archiveHash>; t3 --versiont3 v0.0.42; ready marker records the t3 entry digest. PASS
  2. Warm reuse: t3 mtime unchanged, no re-extraction. PASS
  3. Truncated archive (SHA mismatch), cold cache: exit 1, no runtime dir created. PASS
  4. Valid-SHA non-tar archive: exit non-zero, not promoted. PASS
  5. Archive whose t3 does not run: exit 1, not promoted. PASS
  6. Second candidate (distinct SHA): separate sha256-* runtime dir; both run; no cross-contamination. PASS
  7. Invalidate → reinstall: marker removed, then restored by reinstall. PASS
  8. Prune with 3 candidates: aged, unlocked, non-current candidate removed; current + most-recent previous retained; current still runs. PASS
  9. Staged runtime serve: HTTP 200 on /, clean SIGTERM shutdown; node-pty/build/Release/pty.node present. PASS
  10. First-prompt titles with generateThreadTitles=false in the WSL environment: the setting is packaged (packaged server.asar carries generateThreadTitles ×28, the label Generate thread titles with AI ×2, thread titles ×9; app.asar carries generateThreadTitles ×4). A live provider turn was not executed — no controlled test provider is configured here without credentials. DEFERRED (see below).

Cold-cache + no-ambient-Node evidence: scenarios 1, 2, 5, 9 above all ran with PATH containing no Node and an isolated T3 home.
Fallback/old-runtime audit: a failed install never promotes and never touches an existing ready runtime; the runtime id is sha256-<archiveHash>, so a stale or official runtime is never selected. t3 update inside a distro is the one path that can reach upstream (see escapes).

WinGet pin / feed findings and correction

  • Found T3Tools.T3Code pinned with Pin type: Pinningnot blocking.
  • Corrected with the installed version's syntax (winget 1.29.380): winget pin remove --id T3Tools.T3Code then winget pin add --id T3Tools.T3Code --blocking. Verified via winget pin listPin type: Blocking. Did not test protection by upgrading.

Update-source escapes (for R)

  1. packages/shared/src/cliRelease.ts:8 hardcodes CLI_RELEASE_REPOSITORY = "pingdotgg/t3code". cliReleaseIndexPageUrl (line 101) is not env-overridable, so t3 update always resolves the newest version from upstream; CLI_RELEASE_BASE_URL_ENV only overrides the download base. scripts/install.ps1:19 and scripts/install.sh:20 also default to pingdotgg/t3code.
  2. scripts/build-desktop-artifact.ts:2537 resolveGitHubPublishConfig uses T3CODE_DESKTOP_UPDATE_REPOSITORY or GITHUB_REPOSITORY; if either names upstream, app-update.yml would poll upstream. This fork build correctly ships none (verified: no feed, no pollers).
  3. .github/workflows/release.yml (inherited) depends on upstream runners/relay/Clerk/npm publish — do not enable for the fork.
  4. WSL runtime replacement is installer-driven only (id = archive SHA); the desktop never fetches a WSL runtime over the network.

State preservation

  • Live install untouched: %LOCALAPPDATA%\Programs\t3code still the 0.0.42 fork; running app not restarted or replaced; no real runtime caches deleted (all WSL tests used isolated ~/w-t3/... homes); no live database or settings touched.
  • Windows vs WSL ownership: DesktopBackendManager.ts:91-93 passes WSL env verbatim and deliberately avoids leaking T3CODE_HOME onto /mnt/c/…\.t3, so the WSL backend uses its own Linux T3 home — Windows settings/auth/pairings do not configure the WSL server.

Defects / action items for R

  1. Make the CLI release origin fork-configurable (at minimum CLI_RELEASE_REPOSITORY, ideally the release-index URL too), else t3 update and the install scripts reach upstream.
  2. Keep --wsl-runtime mandatory for Windows (the packaging gate already refuses a missing/invalid archive + SHA sidecar).
  3. Build the Windows t3-resource-monitor.exe with MSVC in the release job (this qualification used mingw locally).
  4. Decide and document the fork update feed (publish fork assets + latest.yml, or keep no-feed builds as done here).

Final-candidate tests still required (after R's release PR)

  • Install R's candidate on a clean Windows profile and drive the GUI WSL backend end-to-end: distro selection (Ubuntu-24.04 vs docker-desktop), cold runtime install, terminal/PTY session, a minimal provider turn, restart/reuse, second-candidate transition, failure recovery.
  • Live first-prompt title smoke with generateThreadTitles=false inside WSL.
  • Signed (Azure Trusted Signing) installer verification; published release assets + SHA256SUMS.

Ownership: landed nowhere new. This is machine-local qualification evidence only; nullStack65/t3code remains R's to edit.

@nullStack65

Copy link
Copy Markdown
Owner Author

R — implementation PR opened

Implementation PR: #5
feat(release): fork release pipeline with fork update isolation

What landed

  • .github/workflows/fork-release.yml: builds one immutable SHA + explicit version into Windows x64 NSIS (with the matching Linux x64 archive embedded as WSL runtime via the existing --wsl-runtime path), Intel macOS x64 DMG, and Linux x64 runtime archive. Reuses release-desktop.yml unchanged. GitHub-hosted standard runners only; build jobs contents: read; publication is a separate contents: write job behind the fork-release environment.
  • Fork version scheme scripts/fork-release-version.ts: plain strictly-increasing X.Y.Z, rejects preview/nightly and build-metadata versions, never downgrades.
  • Update isolation: packages/shared/src/cliRelease.ts defaults the release repository to nullStack65/t3code for both the download base URL and the release-index API (T3CODE_RELEASE_REPOSITORY override), closing the t3 update index gap; install.sh/install.ps1 default to the fork too.
  • Provenance: repository, full source SHA, version, and architecture embedded in the desktop app and the CLI archive (t3code-build-info.json + staged package.json fields); qualify re-verifies the Linux archive and writes SHA256SUMS from final bytes.
  • Unsigned macOS builds ship without an update feed (Squirrel.Mac cannot apply an unsigned update); Windows keeps its feed.

Gate discovered (operator action required)

fork-release.yml is a workflow_dispatch workflow, and GitHub only allows dispatching a workflow that exists on the default branch. It cannot be dispatched from PR #5; a real candidate build requires this PR to be merged to main first (or the workflow file to exist on main). No release is published by PR #5.

Continuing with local validation on PR #5.

@nullStack65

Copy link
Copy Markdown
Owner Author

R — RESULT posted on the implementation PR

RESULT — FORK RELEASE IMPLEMENTATION: #5 (comment)

  • Implementation PR: feat(release): fork release pipeline with fork update isolation #5
  • Base: bcc1a58b19a9d610a4f08fed191a364767bc65b3
  • Head: 89369420870a3086051fb01462193c805ecc2aaa
  • Status: PARTIAL — implemented + locally validated; candidate build and publication gated on merge and native W/M acceptance.
  • Operator gate: fork-release.yml cannot be dispatched until it exists on main (GitHub requires the workflow on the default branch).

@nullStack65

Copy link
Copy Markdown
Owner Author

RESULT — MACOS RELEASE QUALIFICATION

Status: PASS (baseline qualification) with an explicit signing boundary and one deferred owner gate.
Role: M — macOS qualification owner (native build/launch, signing capability, install/recovery experiments). No shared source/build workflow was edited and no competing PR was opened.
Machine: macOS 26.6.2 (25G83), x86_64 — Intel(R) Core(TM) i5-10600 CPU @ 3.30GHz, host Crown-Rain-Gutters.local.
Source: fork nullStack65/t3code main @ bcc1a58b19a9d610a4f08fed191a364767bc65b3; clean checkout /Users/businessaccount/t3-fork-build (origin git@github.com:nullStack65/t3code.git, HEAD bcc1a58b1). Upstream pingdotgg/t3code read-only.
R's implementation PR: #5 (feat(release): fork release pipeline with fork update isolation, head 89369420870a3086051fb01462193c805ecc2aaa when observed).


Toolchain (exact, resolved — not ambient)

  • Node: system v25.6.0 (/usr/local/bin/node); vp runs builds under a managed Node v24.21.0 (repo engines.node = ^24.13.1).
  • pnpm 11.10.0; vite-plus vp (repo-local vite-plus 0.3.3).
  • Rust: rustup stable-x86_64-apple-darwin, rustc 1.98.1 (48a229cea 2026-09-01), cargo 1.98.1 — present at ~/.cargo/bin but not on the default PATH.
  • Xcode: Command Line Tools only (/Library/Developer/CommandLineTools), Apple clang 12.0.0, SDK 11.1 only (MacOSX.sdk -> MacOSX11.1.sdk). No full Xcode, no xcodebuild, no notarytool.
  • Homebrew LLVM 20.1.8 at /usr/local/opt/llvm@20.

Native build requirement — root cause + working recipe (previously undocumented)

The reported "native node-pty/compiler requirement" is now precisely reproduced and fixed:

  1. Apple clang 12 cannot compile node-pty — it rejects Electron's C++20 flag: error: invalid value 'gnu++20' in '-std=gnu++20'.
  2. Homebrew LLVM 20 compiles it but fails to link — its clang++ has a compiled-in default sysroot /Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk (does not exist on this machine); ld: library not found for -lc++. Setting SDKROOT does not override the Homebrew default; only an explicit -isysroot does.
  3. Working lever: the gyp Makefile honours LDFLAGS.target ?= $(LDFLAGS), so LDFLAGS (not just CFLAGS/CXXFLAGS) must carry -isysroot.

Successful build:

vp run dist:desktop:dmg:x64   # -> node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch x64
# env:
PATH="$HOME/.cargo/bin:$PATH"
CC=/usr/local/opt/llvm@20/bin/clang
CXX=/usr/local/opt/llvm@20/bin/clang++
CFLAGS="-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
CXXFLAGS="-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
LDFLAGS="-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"

Resulting pty.node links only /usr/lib/libc++.1.dylib and /usr/lib/libSystem.B.dylib; spawn-helper builds. Both x86_64. Resource-monitor builds from source via cargo on the same SHA.

Artifacts and reproducibility

artifact baseline bcc1a58 (existing) rebuilt bcc1a58 (this run)
T3-Code-0.0.42-x64.dmg sha256 3a08c48c378ba8cbe1db9d05a76305b095e9ddd8bf1c07be36cb9baf5eb62ce0 (140,517,788 B) 8e9748f4960349c6d1ba0624641a26709c65669abbdfff1e214f6879e0ef19f0 (140,517,881 B)
T3-Code-0.0.42-x64.zip sha256 46c3b4fd7b6876fda08b38e83d38beb6ba316e4158488689820e70151ce1a534 5d88b32c9ef990cce221c87b49d6f5bf6a012a2a23deb3826ba36be4e68f7b47
embedded app.asar eb196a81e2fcd3f2ea86801985db9ca68748c04b3c20ed0074deac491f70f330 dba76861e649240ac369fa8bd5cd6bf8c924d34c1bc721b1a6553a0acb542cb5
resource-monitor/t3-resource-monitor fab00ebdc444b00ae2a1295b35dab3910a8a6b3c82a844a2030f5366619eca8e identical fab00ebd…
node-pty/build/Release/pty.node 83859973f42e65f0a70898b25e2d18f2338fdba7089a69221a3180c56c0d8583 c8e81a4dc2ade0fe341fdb3b837d5397a6c09bc480f5e333bebde23f4b09ab55

Reproducibility detail: of the 2,642 files inside app.asar, exactly two differ across the two builds — node-pty/build/Release/pty.node and spawn-helper — and only because they were compiled by different compilers. The other 2,560 in-asar files are byte-identical, including apps/server/dist/bin.mjs, apps/server/dist/client/index.html, and package.json. resource-monitor is byte-identical. Conclusion: the JS/content build is reproducible; the native node-pty binary is not toolchain-independent, so the compiler recipe above must be pinned. Raw DMG/zip bytes differ (DMG/zip metadata), as expected.

Embedded provenance verified in both artifacts: "t3codeCommitHash": "bcc1a58b19a9"; Mach-O executable x86_64; no arm64 slice anywhere in the bundle.

Signing / notarization capability

  • security find-identity -v -p codesigning0 valid identities; no Developer ID Application certificate in any keychain.
  • notarytool unavailable (CLT-only host); stapler present.
  • Artifacts: code object is not signed at all; spctl -a -t execrejected, source=no usable signature.
  • These are unsigned — explicitly not ad-hoc-signed and not Developer-ID/notarized. Ad-hoc (codesign -s -) is possible locally but changes nothing for Gatekeeper on a fresh machine and does not enable updates.
  • Update feasibility: Squirrel.Mac cannot apply an unsigned update, so the built app correctly ships no update feed (app-update.yml absent, no latest*.yml). No signature check or Gatekeeper setting was weakened.
  • Exact remaining signing requirements for the owner/R: Apple Developer Program membership; Developer ID Application cert (team ID); full Xcode or at least notarytool-capable tooling + App Store Connect API credentials; T3CODE_DESKTOP_SIGNED=1 build path (scripts/sign-macos.ts); for passkey builds additionally T3CODE_APPLE_TEAM_ID + a .provisionprofile + Clerk passkey RP domains. No credentials were provisioned, bought, or exported.

Downloaded-bytes / quarantine (SIMULATION — not a real second machine)

Simulated a browser download by copying the DMG to an isolated dir and setting com.apple.quarantine (0081;5f2a1c00;Safari;):

  • spctl -a -t open --context context:primary-signature on the DMG → rejected, no usable signature.
  • hdiutil attach succeeds (CRC verified); the app inside is unsigned; spctl -a -t exec on the copied app → rejected.
  • Direct exec from Terminal of the quarantined app launched and started the server — Terminal exec bypasses LaunchServices Gatekeeper. Finder/open double-click is the path Gatekeeper blocks for unsigned quarantined apps.
  • Manual install path that works now: drag to /Applications; if blocked, right-click → Open, or xattr -dr com.apple.quarantine "<app>" (never disable Gatekeeper globally), then launch.
  • This is a same-machine, isolated-profile simulation. It is not a genuine clean-machine test; a real second Intel Mac and a published candidate are still required for final acceptance.

New-machine install + launch (isolated profile and test install location)

Copied the app to an isolated …/fresh/Applications/ and launched with a minimal environment:
env -i PATH=/usr/bin:/bin HOME=<fresh> T3CODE_HOME=<fresh>/.t3 "<app>/Contents/MacOS/T3 Code (Alpha)".

  • No development tools required (no system Node/npm/cargo/rust/LLVM on PATH). The server runs on Electron's bundled Node.
  • Server started on 127.0.0.1:3774 and wrote state under $T3CODE_HOME/userdata (state.sqlite, secrets/server-signing-key.bin, environment-id, caches). The desktop client auto-paired (auth_sessions = 1). Provider caches enumerated: codex, opencode, claude, grok, cursor, antigravity.
  • Fork/source identification: app.asar/package.json carries t3codeCommitHash = bcc1a58b19a9, and the app sets its About-panel version to that hash (DesktopAppIdentity.configure).
  • Title setting present: UI label "Generate thread titles with AI" and description "When off, new threads use the first prompt as their title instead of asking a model to name them." present in the shipped app.asar (and in the rebuilt DMG); server bundle contains generateThreadTitles.
  • Deterministic-title behavior (gate points): ProviderCommandReactor.ts:968 (initial), :1026 (refinement dispatch), :1068 (manual regenerate) all early-return when generateThreadTitles is off, so the first-prompt title is retained.
    • Automated evidence at bcc1a58 (stable TMPDIR): apps/server ProviderCommandReactor.test.ts73 passed (includes "keeps the deterministic first-prompt title when AI title generation is disabled" and "clears manual title regeneration without generating when disabled"); packages/contracts settings.test.ts145 passed (default true, decode, patch).
  • LIVE UI deterministic-title acceptance: NOT executed (deferred). The new-thread flow requires a workspace (native folder picker) and a configured provider; the isolated fresh profile has neither, and the deterministic title is client-derived, so it cannot be exercised server-side. I did not toggle the live user's setting or create threads in the live profile. Owner/R gate: run a UI-driven test on a real profile with a project after the first candidate is published.

Update-source isolation

  • Live install: brew list --cask no longer contains t3-code; the installed fork app has no app-update.yml and no latest*.yml → desktop auto-update cannot return to upstream.
  • Residual upstream runtime references still present in the built app (defects for R):
    1. apps/server/src/provider/ModelManifest.ts:39-40https://raw.githubusercontent.com/pingdotgg/t3code/main/apps/server/src/provider/model-manifest.json (runtime model-manifest fetch). Not covered by PR feat(release): fork release pipeline with fork update isolation #5.
    2. packages/shared/src/cliRelease.ts:8,57pingdotgg/t3code for the CLI release index/download — PR feat(release): fork release pipeline with fork update isolation #5 fixes this; verify it is present at the built candidate SHA (t3 update isolation).
    3. apps/web/src/components/desktopUpdate.logic.ts:5https://github.com/pingdotgg/t3code/releases (desktop release-history link).
    4. apps/server/src/cli/triagePrompt.ts (CLI triage prompt URLs), apps/mobile/app.config.ts (owner pingdotgg), docs/comments.
  • Build had no .env, so no Clerk publishable key or relay URL is baked and relay.t3.codes is absent. Local providers work; confirm whether fork desktop releases should bake the public Clerk/relay identifiers.

State-preserving replacement + rollback (isolated; synthetic state)

Procedure deliberately staged and validated the candidate before touching the install:

  1. Staged new app; validated x86_64 + t3codeCommitHash=bcc1a58b19a9 + unsigned; launched against the isolated T3CODE_HOME → server ready in 6 s; quit.
  2. Swap: mv previous app to a backup dir (never rm), then ditto staged app into the install path.
  3. Launched installed app → server ready in 3 s. Stable state fingerprint (all userdata files except logs/*, state.sqlite*, server-runtime.json) identical (f3508084…); projects/threads/auth_sessions counts unchanged.
  4. Rollback: mv the swapped-in app back to staging (kept as candidate) and mv the previous app back into the install path; launched → server ready in 3 s; stable fingerprint still f3508084…; counts unchanged.
  • No rm-then-copy; the previous working app was never removed before the replacement was staged and validated. The live /Applications installation was not touched — this session is still hosted by PID 24461/24489 running bcc1a58b1.
  • Data/schema constraint (owner gate): the live DB has 53 applied migrations. Old/new here were the same schema version, so rollback was clean. Cross-version rollback is NOT proven — an older app may not understand a newer schema. Define/verify a forward-only migration + rollback policy before publishing candidates.

Is this machine suitable for authorized isolated release builds?

  • Yes for unsigned Intel x64 DMG packaging, on clean local disk, with the documented toolchain env above. It is not registered as a runner and is not exposed to public PR jobs.
  • No signing/notarization capability (no Developer ID cert, no notarization tooling/credentials).
  • Apple Silicon: not qualified here. macos-15-intel GitHub-runner Intel capacity must be confirmed by a real dispatch; a cross-built-but-unexecuted arm64 artifact must not be labelled tested.
  • Disk caution: /System/Volumes/Data was at ~12–14 GiB free; a clean stage plus TMPDIR is required. Free space before full builds.

Defects for R (source edits belong on PR #5)

  1. ModelManifest.ts still fetches the model manifest from upstream pingdotgg/t3code — add a fork repository override as done for cliRelease.ts.
  2. desktopUpdate.logic.ts release-history link still points at upstream.
  3. triagePrompt.ts upstream URLs (low priority).
  4. Pin/document the macOS native-node-pty toolchain (Homebrew LLVM 20 + explicit -isysroot on CFLAGS, CXXFLAGS, and LDFLAGS), otherwise the build fails on this host or emits toolchain-dependent, non-reproducible native binaries. Add a preflight and/or document it in docs/operations/fork-release.md.
  5. Decide whether fork desktop builds should bake the public Clerk/relay identifiers (currently none, because there is no .env).
  6. fork-release.yml dispatch requires the workflow to exist on main (already raised by R); also confirm macos-15-intel runner availability via a real dispatch.

Outstanding final-candidate / owner gates

  • Merge PR feat(release): fork release pipeline with fork update isolation #5 so fork-release.yml can be dispatched; produce the first candidate artifacts.
  • Then, on macOS: downloaded-bytes quarantine/Gatekeeper test on the published bytes; clean-profile install; live UI deterministic-title test; provider smoke; state-preserving upgrade from the live 0.0.42 install; rollback/schema policy.
  • Signing/notarization decision + credentials (owner).
  • Apple Silicon: separate native-capacity gate; do not label cross-built artifacts as tested.
  • Publish and verify SHA256SUMS against the downloaded bytes.

No secrets, auth tokens, pairing URLs, or private state are included in this receipt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant