feat(dev): dev server pairing and seed utilities#4557
Conversation
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
ApprovabilityVerdict: Needs human review This PR introduces substantial new developer tooling features including database seeding utilities with complex SQLite manipulation, new CLI commands, and changes to session cookie scoping behavior. The scope of new functionality and auth-adjacent changes warrant human review. You can customize Macroscope's approvability policy. Learn more. |
|
Picked this up, reviewed it, and pushed fixes in 331fc71. Review found five ways a seeded database could describe something the copy cannot back up. All are in the
Turns copied mid-flight stayed Attachment metadata was copied without the files. Orphaned provider bindings survived.
Also corrected a comment: it claimed Verification: 33 tests pass (9 new covering the guard, turn settling, streaming, attachment ids, and binding cleanup), scripts typecheck and lint clean. |
|
Second round pushed in 73669b3, covering the Macroscope comment and findings from a follow-up review of the pairing path.
Turns copied in the The source was read across a dozen implicit transactions while the installed app may be writing to it, so a project could vanish between the query that selected it and the copy that read its row — leaving an orphaned thread, with no foreign keys to catch it. Now one deferred read transaction pinning every query to a single snapshot.
Verification: 38 tests pass, full-repo typecheck reports 0 errors, lint clean on changed files. |
edc8e4f to
dbad3bc
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
scripts/mobile-showcase-environment.ts (1)
2-2: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFinish the dedup:
PROJECTION_TABLES_IN_DEPENDENCY_ORDERis also exported now.
seedDatabasestill inlines the same nine-table delete list that the new module exports, so the two can drift the moment a projection table is added.♻️ Import the shared list too
-import { PROJECTOR_NAMES } from "./lib/projection-tables.ts"; +import { + PROJECTION_TABLES_IN_DEPENDENCY_ORDER, + PROJECTOR_NAMES, +} from "./lib/projection-tables.ts";Then in
seedDatabase:for (const table of PROJECTION_TABLES_IN_DEPENDENCY_ORDER) { database.exec(`DELETE FROM ${table}`); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/mobile-showcase-environment.ts` at line 2, Update the imports in scripts/mobile-showcase-environment.ts to include PROJECTION_TABLES_IN_DEPENDENCY_ORDER, then replace seedDatabase’s inline projection-table delete list with iteration over that shared exported list while preserving the existing DELETE execution.scripts/dev-seed.ts (1)
266-276: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
catch: (cause) => cause as DevSeedErroris an unchecked cast.
seedDevDatabasewraps its own failures today, so this holds in practice, but any future throw that isn't aDevSeedError(or isn't anErrorat all) reaches thetapErrorat Line 312-315 and dereferences.messageon it. Normalizing here keeps the error channel type honest.♻️ Normalize unexpected causes
- catch: (cause) => cause as DevSeedError, + catch: (cause) => + cause instanceof DevSeedError + ? cause + : new DevSeedError(`could not seed the dev database: ${String(cause)}`),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/dev-seed.ts` around lines 266 - 276, Replace the unchecked cast in the Effect.try catch handler around seedDevDatabase with normalization that preserves existing DevSeedError instances and converts unexpected thrown values into a DevSeedError-compatible error. Ensure non-Error causes are also safely represented so the downstream tapError handling can access message without throwing.scripts/lib/dev-seed.test.ts (1)
545-572: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider marking the 32k-thread fixture as a slow test.
Building 32,766 thread and turn rows on every run of this file is the one materially slow case here. A
it.skipIf/tag or a longer explicit timeout keeps the focused-test workflow snappy without losing the ceiling guarantee in CI.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/lib/dev-seed.test.ts` around lines 545 - 572, Mark the maximum-thread-limit test identified by its “seeds at the maximum thread limit without exhausting SQL variables” description as slow, using the project’s existing skip/tag mechanism or an explicit extended timeout. Preserve its CI execution and the existing ceiling assertions while keeping focused local test runs fast.packages/tailscale/src/tailscale.ts (1)
219-228: 📐 Maintainability & Code Quality | 🔵 TrivialMinor:
stderrPreviewOf(stderr)computed twice per call site.Both
readTailscaleStatusandrunTailscaleCommandcallstderrPreviewOf(stderr)once in the ternary condition and again in the object body. Hoisting into a local avoids the redundant (if cheap) call and reads slightly cleaner.♻️ Proposed dedup
- ...(stderrPreviewOf(stderr) !== undefined - ? { stderrPreview: stderrPreviewOf(stderr) } - : {}), + ...(preview !== undefined ? { stderrPreview: preview } : {}),(with
const preview = stderrPreviewOf(stderr);added above each call site)Also applies to: 286-295
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/tailscale/src/tailscale.ts` around lines 219 - 228, Deduplicate stderrPreviewOf(stderr) in both readTailscaleStatus and runTailscaleCommand by assigning its result to a local preview variable before constructing TailscaleCommandExitError, then reuse that variable for the conditional property and stderrPreview value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.agents/skills/test-t3-app/SKILL.md:
- Around line 51-58: Update the later troubleshooting guidance that recommends
manually minting replacement tokens to direct agents to run the existing `bun
run dev:pair` workflow instead, preserving the documented `--base-dir` usage
when applicable and removing the obsolete manual-token recovery instructions.
In `@docs/reference/scripts.md`:
- Around line 54-55: Update the runtime-state documentation to describe probing
both dev and userdata for the directory containing the active database,
resolving dev/userdata ties by modification time. In docs/reference/scripts.md
at lines 54-55, replace the userdata-first wording; in
.agents/skills/test-t3-app/SKILL.md at line 58, explain that pairing checks both
directories and applies the same active-state selection rule.
- Around line 4-6: Standardize the documented development workflow on the
repository’s bun wrappers: in docs/reference/scripts.md lines 4-6, replace the
vp run dev, vp run dev:share, vp run dev:pair, and vp run dev:seed examples with
their bun run equivalents while preserving the existing options and
descriptions; in .agents/skills/test-t3-app/SKILL.md line 16, replace vp run dev
with bun run dev and keep the sharing variant as bun run dev:share.
---
Nitpick comments:
In `@packages/tailscale/src/tailscale.ts`:
- Around line 219-228: Deduplicate stderrPreviewOf(stderr) in both
readTailscaleStatus and runTailscaleCommand by assigning its result to a local
preview variable before constructing TailscaleCommandExitError, then reuse that
variable for the conditional property and stderrPreview value.
In `@scripts/dev-seed.ts`:
- Around line 266-276: Replace the unchecked cast in the Effect.try catch
handler around seedDevDatabase with normalization that preserves existing
DevSeedError instances and converts unexpected thrown values into a
DevSeedError-compatible error. Ensure non-Error causes are also safely
represented so the downstream tapError handling can access message without
throwing.
In `@scripts/lib/dev-seed.test.ts`:
- Around line 545-572: Mark the maximum-thread-limit test identified by its
“seeds at the maximum thread limit without exhausting SQL variables” description
as slow, using the project’s existing skip/tag mechanism or an explicit extended
timeout. Preserve its CI execution and the existing ceiling assertions while
keeping focused local test runs fast.
In `@scripts/mobile-showcase-environment.ts`:
- Line 2: Update the imports in scripts/mobile-showcase-environment.ts to
include PROJECTION_TABLES_IN_DEPENDENCY_ORDER, then replace seedDatabase’s
inline projection-table delete list with iteration over that shared exported
list while preserving the existing DELETE execution.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1139449d-5adc-49f6-a961-eb622b3e4674
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (34)
.agents/skills/test-t3-app/SKILL.md.agents/skills/test-t3-app/references/sqlite-fixtures.mdAGENTS.mdapps/server/src/auth/EnvironmentAuth.test.tsapps/server/src/auth/EnvironmentAuth.tsapps/server/src/auth/EnvironmentAuthPolicy.test.tsapps/server/src/auth/EnvironmentAuthPolicy.tsapps/server/src/auth/PairingGrantStore.tsapps/server/src/auth/SessionStore.tsapps/server/src/auth/utils.tsapps/server/src/cli/auth.test.tsapps/server/src/cli/auth.tsapps/server/src/config.tsapps/server/src/http.tsapps/server/src/serverRuntimeState.test.tsapps/server/src/serverRuntimeState.tsapps/web/vite.config.tsdocs/reference/scripts.mdpackage.jsonpackages/shared/package.jsonpackages/shared/src/devProxy.tspackages/tailscale/src/tailscale.tsscripts/dev-runner.test.tsscripts/dev-runner.tsscripts/dev-seed.tsscripts/lib/dev-seed.test.tsscripts/lib/dev-seed.tsscripts/lib/dev-share.test.tsscripts/lib/dev-share.tsscripts/lib/projection-tables.tsscripts/lib/server-runtime-probe.test.tsscripts/lib/server-runtime-probe.tsscripts/mobile-showcase-environment.tsscripts/package.json
3fa6578 to
97401b1
Compare
|
On the docstring coverage warning: added docs to the exported surface in 33fcf80 — the parts a caller genuinely needs to know. Specifically that I stopped short of the 80% threshold deliberately. The remaining undocumented declarations are things like Happy to revisit if the 80% target is a deliberate repo standard rather than a CodeRabbit default — that's a maintainer call, not one I want to make unilaterally on someone else's codebase. |
Adding "pending" to the settled states bound them as parameters, costing two bindings on top of one per thread. `--threads` is capped at 32766 — SQLite's ceiling — so the documented maximum landed exactly two over and threw "too many SQL variables", after the target had already been emptied. Inline the literals; they are internal constants, never input. The regression test seeds at the cap for real rather than asserting the statement text, since the property is that no code path adds a binding. Fixture rows are batched into one transaction and the scale case builds thread rows only, keeping it near a second. Caught by Macroscope review. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The cap was declared in the CLI while the constraint belongs to the seeder's statements, and the number appeared in three places. That split is the mechanism behind the last regression: a change to a statement in the library silently invalidated a bound stated in the CLI, and the mismatch only surfaced at the maximum — after the target was emptied. Move it to `MAX_SEED_THREAD_LIMIT` beside the statements that have to satisfy it, have the flag derive its bound from there, and check it in `seedDevDatabase` before anything is opened so a caller bypassing the flag fails while the target is still intact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three findings from Macroscope review: - Settling `pending` rows to `interrupted` was the wrong verb. The server deletes them (`clearPendingProjectionTurnsByThread`) once the turn starts or the thread stops, and never settles one in place. They also cannot be settled coherently: `turn_id` is NULL, so an "interrupted" pending row is a terminal turn with no id, a shape no real turn has. Now deleted, matching the server's predicate exactly so a checkpoint row is never caught by it. - `COALESCE(completed_at, requested_at)` preserved a stale completion on a running turn. One can carry a completion from an earlier settle that a later checkpoint reverted to "running" — the `...existingTurn` spread in ProjectionPipeline keeps the old value — and dating the interruption to that checkpoint gives a wrong duration. Overwritten unconditionally. - The CLI hard-coded `<base>/userdata/state.sqlite` while a dev server under an implicit dev home stores state in `<base>/dev`. A main-checkout `bun run dev` was therefore unseedable, and said so by claiming no database existed and suggesting the user start the server that was already running. Source and target now follow whichever directory holds a database, `userdata` breaking the tie; attachments follow the same directory. Verified both a dev-only target and the precedence order. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Self-review of the settle path, which has been the source of every regression in this branch. The server filters `turnId !== null && state === "running"` before settling; this filtered only on state. No row is both null-id and running today — the sole null-id insert writes 'pending' — so this changes no behavior. It is the invariant the UPDATE already depends on, written down where it is relied upon, and it keeps the two predicates directly comparable if a null-id running row ever becomes possible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two more from Macroscope review: - `ORDER BY COALESCE(...)` throws on a source down to one recency column. SQLite requires at least two arguments, so the pre-017 schema the column filter exists to support was exactly the case that aborted. Emit the column directly when only one survives the filter. - `seedDevDatabase` accepted non-positive limits. Both are bound into `LIMIT ?`, where SQLite reads a negative as "no limit" — a caller bypassing the CLI flags silently got a full-table copy instead of the bounded one the options contract promises. Rejected before the target is touched, alongside the existing upper-bound check. Both regression tests confirmed failing without their fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Preferring `userdata` whenever both directories exist was wrong for the case that motivated resolving at all: an implicit `bun run dev` stores state in `dev`, so a base directory that had also been used with an explicit `--home-dir` would seed the database the running server does not open — looking like the seed did nothing. Neither directory is categorically right, so the tie now goes to whichever was written most recently: the one the last server actually used. `userdata` still wins an equal or unavailable mtime. The chosen path is already printed, so a wrong guess is visible rather than silent. Verified both directions against a target holding both databases. Caught by Cursor Bugbot. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rebasing onto the updated #4556 turned up two doc drifts: the base moved the script list from `vp run` to `bun run`, and the state-directory paragraph still described the fixed `userdata` preference that the mtime tiebreak replaced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CodeRabbit flagged docstring coverage. Added docs where a caller would actually need them — that `seedDevDatabase` is destructive and not incremental, that it validates before opening anything so a rejected run leaves the target intact, that the caller still owns the attachment files, and that `DevSeedError.hint` is the actionable half the CLI prints separately. Left the self-describing internals (`placeholders`, `hasTable`, `columnsOf`, `stateDbPath`) alone: the surrounding scripts/ code documents about 2% of its declarations, so padding to an 80% tool default would read as noise rather than convention. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two findings from Macroscope review: - The running-server guard was check-then-act. A copy takes seconds, and nothing stopped a server starting after the check returned None — the seed would then replace the projections and event log underneath it, which is exactly what the guard exists to prevent. `seedDevDatabase` now takes an optional probe and re-runs it immediately before COMMIT, where a rollback still costs nothing. This narrows the window to the commit itself rather than closing it: a filesystem read and a SQLite transaction share no lock, so a server starting in that last instant is still possible. Documented as such rather than claimed as airtight. - The state-dir tiebreak read `state.sqlite` mtime, but the server runs SQLite in WAL mode, where writes land in `state.sqlite-wal` and the main file can stay untouched until a checkpoint. Verified: 50 writes left the main mtime unchanged while the sidecar advanced. So a busy directory lost the tie to a dormant one, and the seed overwrote the wrong database. Recency is now the newer of the two files. Both verified end-to-end: a backdated main file with a fresh -wal now wins the tie, and a probe returning a pid mid-copy leaves the target's rows and event log intact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sync probe added for the pre-commit re-check duplicates the Effect one's rules by hand — positive pid, live process, unparseable means no. Two implementations of one rule drift apart silently, and this branch has already produced several bugs of exactly that shape. Asserts every case against both probes rather than trusting them to agree: live in either state dir, dead pid, pid 0 and -1, malformed JSON, and JSON with no pid. Confirmed the parity test fails when the sync probe's positive-pid guard is removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The test schema omitted `projection_turns.checkpoint_turn_count`, which is the column `dropPendingTurnStarts` keys its "spare checkpoint rows" guard on. Every existing test therefore ran the degraded path where the clause is dropped for schema drift, so the guard's real behavior was never exercised. Added the column (migration 005 has it) and a case covering what the clause is for: a pending row carrying a checkpoint survives the delete. Confirmed the case fails when the clause is removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Auditing the fixture against the real migrations (the method that found the missing checkpoint column) turned up a second gap: the test schema omitted `projection_thread_activities.sequence`, and the copy's per-thread cap ordered on `created_at` alone. The app reads these back ordered by `sequence` first, then `created_at` (ProjectionSnapshotQuery). A burst of tool activity lands many rows on one timestamp, so capping on the timestamp alone cuts arbitrarily among them — demonstrated with four tied rows where "keep the newest two" kept the two the timeline treats as oldest. Ordered to match, falling back to the timestamp when the source predates migration 008. Fixture gained the column, and the regression test fails against the old ordering. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Completing the fixture audit: `orchestration_command_receipts` is one of the three tables the seed clears, but the fixture never created it, so that delete only ever ran its table-missing skip path. Added the table (migration 002) with a receipt row, and extended the event-log test to assert receipts go too — a receipt left behind records a command whose events the seed deleted, so re-issuing that command would be treated as already applied and produce nothing. Confirmed the assertion fails when the delete is removed. The audit is now complete: every table the seed writes or clears exists in the fixture, and every column it keys on is present. The remaining schema gaps are columns copied generically by column intersection, where no seeder behavior depends on them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two Cursor Bugbot findings on the merge, plus the simplification they enabled: - The activity cap ordered `created_at` before `sequence`; the app ranks `sequence` first. The keys disagree outright under clock skew — a higher-sequence activity can carry an older timestamp — and then the cap keeps rows the timeline treats as oldest. Reordered to match, with the regression test now covering a skewed row, not just a tied one. - The running-server guard blocked on a live server in either state directory while the seed touches exactly one. A server using only the other directory's database now no longer refuses the seed; both the pre-copy check and the pre-commit re-check take the resolved state dir. - Scoping the probe meant threading one rule through both variants, so they now share a single implementation: the sync function reads and checks, and the Effect wrapper just lifts it. That deletes the schema decoder the Effect path used, removes the duplicated parsing, and makes the parity the tests pin structural rather than incidental. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Macroscope found a bypass in the shared-home refusal: the guard canonicalized the *base* directory, but a base can canonicalize to itself while its `userdata` or `dev` subdirectory is a symlink into `~/.t3`. The base comparison then passes and the seed destructively replaces the real shared database — the exact accident the guard exists to refuse. Reproduced with a symlinked fixture before fixing. The database path is what gets opened, so it is what gets checked now: canonicalize the resolved db path and refuse when it lies under the canonical shared home. The same-source check moves to db paths for the same reason. Guard ordering shifts after state-dir resolution, which the guard now depends on; the missing-target check runs first either way. Verified all three ways: the symlinked base is refused with the shared database untouched, a direct --to at the shared home is still refused, and a legitimate worktree seed still succeeds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cursor caught a consequence of moving the shared-home guard after path resolution: the missing-target check ran first, so a shared home with no database yet got "start the dev server (`bun run dev`), then seed" — advice that creates the database in the shared home and walks the user into the exact write the guard refuses. The refusals run first again. That surfaced why the previous ordering existed: realPath fails on a missing final component, and falling back to the literal path would lose symlink resolution precisely when the bad advice would be given. Canonicalize the directory and re-join the filename instead, so a symlinked state dir is resolved whether or not the database behind it exists yet. Verified all three ways: a shared home without a database is refused as shared-home, a symlinked base without a database behind it is refused as shared-home, and a legitimate empty target still gets the missing-target hint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two Cursor findings on the previous guard fix: - `canonicalDbPath` resolved only the directory and re-joined the filename, so a symlink at the *file* — `state.sqlite` itself linking into the shared home — passed the guard. Reproduced: the seed replaced the shared database through the link. Resolve the full path first (the file can be the link), falling back to directory-resolution only when the file does not exist yet, which is the one case a full resolution cannot handle and the one where the missing-target advice would create the database in the shared home. - With --from and --to aliasing the same base and no database behind them, same-source fired before the existence check, hiding the actionable "start the dev server" guidance. The existence check now runs between the shared-home refusal (which must stay first — its advice is exactly wrong for the shared home) and same-source. Verified seven ways: symlinked file refused with the shared rows untouched, symlinked directory still refused, shared home with and without a database refused as shared-home, aliased-empty reported as missing-target, aliased-with-database as same-source, and a legitimate seed unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3e1cce1 to
2187054
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2187054. Configure here.
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>

Note
Dev mode overhaul — PR 3 of 3. The stack moves from worktree-isolated dev state → single-origin and Tailscale sharing → pairing and seed utilities. This final PR is stacked on #4556.
What
dev:pairto find a live dev server’s recorded state and issue a fresh pairing URL with the correct web origin.dev:seedto copy recent projection data into an isolated dev database while refusing to write to the shared home.Why
Isolated worktree databases are intentionally empty, and replacing a consumed pairing URL previously required reconstructing internal ports, state paths, and origins by hand. These are useful developer conveniences, but they are not required for the hosting fix itself.
Impact
This is PR 3 of 3 and is stacked on #4556. Keeping these optional utilities last lets the core isolation and sharing changes land without the larger data-copy implementation.
Verification
vp test run scripts/lib/dev-seed.test.ts apps/server/src/cli/auth.test.ts apps/server/src/serverRuntimeState.test.ts(24 tests)Note
Add dev server pairing URL command and database seeding CLI with tailnet lease management
dev:pairnpm script (t3 auth pairing url) that discovers a live dev or userdata server runtime state and prints a one-time pairing URL, preferring the dev instance when both are running.dev:seednpm script (scripts/dev-seed.ts) that seeds recent projects, threads, messages, turns, sessions, and activities from a source T3 data directory into a target dev database; refuses to run against a live server or a path inside the shared home.acquireDevShareatomically claims a per-port owner file before publishing, andcleanupOwnedDevSharerestores a successor's mapping if ownership changes during teardown.findRunningServerPidin scripts/lib/server-runtime-probe.ts to synchronously detect live server PIDs fromserver-runtime.jsonstate files, used by both the seed CLI and the pairing URL command.resolveSessionCookieNamedrops the port suffix for desktop (non-dev) sessions; cookie name is nowt3_sessioninstead oft3_session_<port>whendevUrlis absent, affecting existing desktop sessions on upgrade.Macroscope summarized 6588b84.
Note
Medium Risk
Desktop and hosted sessions now use a single cookie name (users may need to re-pair), while
dev:seedis destructive to target projections/event logs though guarded against the shared home and live servers.Overview
Dev pairing and seeding —
bun run dev:pair(auth pairing url) locates a live server by readingserver-runtime.jsonunderdev/userdata, verifies the recorded PID is still alive, and mints a one-time pairing URL using the stored web origin (including tailnetdevUrl).bun run dev:seedcopies a capped slice of projection data (and attachment files) from~/.t3into a worktree-isolated database, with guards against the shared home (including symlinked paths), same-source seeds, and seeding while a server holds the target DB open (with a late re-check before commit).Runtime state and config — Persisted runtime state now optionally records
devUrl; stale files from crashed processes are ignored via PID liveness probing.deriveServerPathsaccepts an explicitstateDirso pairing tooling targets the same database the running server uses.Auth cookies —
resolveSessionCookieNameno longer special-cases desktop: production/desktop uset3_session; only servers with adevUrlget port-scopedt3_session_<port>.Tailscale
--share— Dev sharing uses file leases (acquireDevShare/cleanupOwnedDevShare) so one runner’s exit does not tear down another’s mapping; cleanup can restore a mapping if ownership changed mid-flight.Docs and tests — Agent skills and
scripts.mddocumentbun run dev,dev:pair, anddev:seed; sqlite fixture docs noteprojection_statecursors; large test coverage for seeding, auth CLI discovery, runtime liveness, and share leases.Reviewed by Cursor Bugbot for commit 6588b84. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Documentation