Merge remote-tracking branch 'origin/main' into duncan/workspace-scoped-agent-store

* origin/main:
  fix(desktop): bind presence retry timers (#6213)
  ci: make file-size policy a first-class gate (#6187)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
Duncan
2026-08-18 10:05:32 -04:00
10 changed files with 95 additions and 32 deletions

View File

@@ -31,6 +31,7 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 2
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
with:
@@ -50,8 +51,6 @@ jobs:
- 'scripts/normative-corpus.json'
- 'justfile'
desktop:
- 'scripts/check-file-sizes-core.mjs'
- 'scripts/check-file-sizes-core.test.mjs'
- 'scripts/model-capabilities.json'
- 'scripts/normative-corpus.json'
- 'desktop/**'
@@ -60,13 +59,9 @@ jobs:
desktop-rust:
- 'desktop/src-tauri/**'
web:
- 'scripts/check-file-sizes-core.mjs'
- 'scripts/check-file-sizes-core.test.mjs'
- 'web/**'
- 'pnpm-lock.yaml'
mobile:
- 'scripts/check-file-sizes-core.mjs'
- 'scripts/check-file-sizes-core.test.mjs'
- 'mobile/**'
- 'scripts/mobile-release.sh'
- 'scripts/mobile-worktree-overrides.sh'
@@ -92,8 +87,8 @@ jobs:
scripts/test-mobile-release-candidate-publisher.sh
- name: Mobile worktree identity contract
run: scripts/test-mobile-worktree-overrides.sh
- name: File size ratchet unit tests
run: node --test scripts/check-file-sizes-core.test.mjs
- name: File size policy
run: just file-size-check
rust-lint:
name: Rust Lint
@@ -896,8 +891,6 @@ jobs:
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ hashFiles('mobile/pubspec.lock') }}
- name: File size ratchet
run: node mobile/scripts/check-file-sizes.mjs
- name: Format check
run: cd mobile && dart format --output=none --set-exit-if-changed .
- name: Analyze

View File

@@ -120,14 +120,14 @@ Run `just test` for integration tests if you touched `buzz-relay`,
formatting via `stage_fixed`. Pre-commit runs fix variants in parallel (Rust
fmt, Tauri Rust fmt, desktop biome fix, web biome fix, mobile dart format).
Auto-fixable issues are fixed and re-staged; unfixable lint issues block the
commit. **Pre-push hooks** run clippy (workspace + Tauri), desktop TypeScript
typechecking (`tsc --noEmit`), and fast unit tests in parallel (Rust, desktop
JS, Tauri Rust, mobile Flutter) — no overlap with pre-commit. Builds are
CI-only. Run `just fix-all` to auto-fix all formatting in one shot. Run
`just ci` for the full local gate. Run `just hooks` to
re-install hooks after env changes. Before agents run Git or hooks, activate the
repo's Hermit environment (`. ./bin/activate-hermit`); do not rewrite hook
commands to compensate for an unconfigured shell `PATH`.
commit. **Pre-push hooks** run the repository-wide differential file-size gate,
clippy (workspace + Tauri), desktop TypeScript typechecking (`tsc --noEmit`),
and fast unit tests in parallel (Rust, desktop JS, Tauri Rust, mobile Flutter)
— no overlap with pre-commit. Builds are CI-only. Run `just fix-all` to auto-fix
all formatting in one shot. Run `just ci` for the full local gate. Run `just
hooks` to re-install hooks after env changes. Before agents run Git or hooks,
activate the repo's Hermit environment (`. ./bin/activate-hermit`); do not
rewrite hook commands to compensate for an unconfigured shell `PATH`.
**Commit with `git commit -s`.** The required **DCO Check** fails any PR with a commit missing a `Signed-off-by` trailer, and `just hooks` installs a `commit-msg` hook that adds it to commits you create locally (`git rebase` and `git cherry-pick` still need `--signoff`) — if you build commit commands programmatically, include `-s` every time. To repair a branch that already has unsigned commits: `git rebase --signoff main`, then force-push.
@@ -566,10 +566,10 @@ The mobile app lives in `mobile/` — a Flutter app using Riverpod + Hooks.
- **Keep widgets small and composable.** One public widget per file; push
private sub-widgets (`_Foo`) into sibling `part` files under a
`<page>/` folder rather than growing the page file. Hard ceiling:
**1000 lines/file**, enforced by `mobile/scripts/check-file-sizes.mjs` via
`just mobile-check` (runs in `just check` + pre-push, mirroring desktop/web).
If the guard trips, **split the file — never bump the limit or add an
override to slip under it.**
**1000 lines/file**, enforced across Desktop, Web, and Mobile by the
repository-level `just file-size-check` gate (`just check`, CI, and every
pre-push). If the guard trips, **split the file — never bump the limit or add
an override to slip under it.**
- Feature modules must not import from other feature modules — only from
`shared/`.
- Use `Grid` tokens for spacing, `Radii` for border radius.

View File

@@ -91,8 +91,17 @@ build:
build-release:
cargo build --workspace --release
# Run repo lint and formatting checks
check: fmt-check clippy desktop-check desktop-tauri-fmt-check desktop-tauri-clippy web-check mobile-check
# Run repo lint, formatting, and repository policy checks
check: fmt-check clippy desktop-check desktop-tauri-fmt-check desktop-tauri-clippy web-check mobile-check file-size-check
# Run the repository-wide differential file-size ratchet and its policy tests.
# The ratchet inspects only files changed from the merge base, so this stays
# cheap enough to run unconditionally without duplicating path filters.
file-size-check:
node --test scripts/check-file-sizes-core.test.mjs
node desktop/scripts/check-file-sizes.mjs
node web/scripts/check-file-sizes.mjs
node mobile/scripts/check-file-sizes.mjs
# Format all Rust code
fmt:
@@ -120,7 +129,7 @@ desktop-check:
# Fix desktop lint and format issues
desktop-fix:
cd {{desktop_dir}} && pnpm exec biome check --write . && pnpm check:file-sizes
cd {{desktop_dir}} && pnpm exec biome check --write .
# Run desktop TS helper unit tests
desktop-test:
@@ -641,7 +650,7 @@ web-check:
# Fix web lint and format issues
web-fix:
cd {{web_dir}} && pnpm exec biome check --write . && pnpm check:file-sizes
cd {{web_dir}} && pnpm exec biome check --write .
# Run web TypeScript checks
web-typecheck:
@@ -673,7 +682,7 @@ mobile-fix:
# Run mobile lint and format checks
mobile-check:
unset GIT_DIR GIT_WORK_TREE; cd {{mobile_dir}} && dart format --output=none --set-exit-if-changed . && flutter analyze && node ./scripts/check-file-sizes.mjs
unset GIT_DIR GIT_WORK_TREE; cd {{mobile_dir}} && dart format --output=none --set-exit-if-changed . && flutter analyze
# Run mobile tests
mobile-test:

View File

@@ -12,7 +12,7 @@
"check:px-text": "node ./scripts/check-px-text.mjs",
"check:pubkey-truncation": "node ./scripts/check-pubkey-truncation.mjs",
"lint": "biome lint .",
"check": "biome check . && pnpm check:file-sizes && pnpm check:px-text && pnpm check:pubkey-truncation",
"check": "biome check . && pnpm check:px-text && pnpm check:pubkey-truncation",
"format": "biome format --write .",
"test": "node --import ./test-loader.mjs --experimental-strip-types --test \"src/**/*.test.mjs\"",
"preview": "vite preview",

View File

@@ -75,6 +75,41 @@ test("a stale async open is closed and never installed", async () => {
reconciler.dispose();
});
test("default timers preserve their global receiver when scheduling retries", async () => {
const originalSetTimeout = globalThis.setTimeout;
const originalClearTimeout = globalThis.clearTimeout;
const timers = [];
const cleared = [];
globalThis.setTimeout = function (callback) {
assert.equal(this, globalThis);
timers.push(callback);
return timers.length;
};
globalThis.clearTimeout = function (timer) {
assert.equal(this, globalThis);
cleared.push(timer);
};
try {
const reconciler = new PresenceSubscriptionReconciler({
open: async () => {
throw new Error("relay unavailable");
},
});
reconciler.setAuthors([A]);
await Promise.resolve();
assert.equal(timers.length, 1);
reconciler.dispose();
assert.deepEqual(cleared, [1]);
} finally {
globalThis.setTimeout = originalSetTimeout;
globalThis.clearTimeout = originalClearTimeout;
}
});
test("failed replacement preserves the previous subscription and retries", async () => {
const timers = [];
const actions = [];

View File

@@ -41,8 +41,11 @@ export class PresenceSubscriptionReconciler {
this.retryDelay =
options.retryDelay ??
((attempt) => Math.min(1000 * 2 ** attempt, 30_000));
this.setTimer = options.setTimer ?? setTimeout;
this.clearTimer = options.clearTimer ?? clearTimeout;
this.setTimer =
options.setTimer ??
((callback, delayMs) => globalThis.setTimeout(callback, delayMs));
this.clearTimer =
options.clearTimer ?? ((timer) => globalThis.clearTimeout(timer));
}
setAuthors(authors: string[]) {

View File

@@ -6,6 +6,9 @@
# changes, though CI's Desktop Core job does. Those commands are pure TS
# (biome + tsc + node:test) with no Rust dependency, so the extra trigger
# would be spurious locally.
# - The repository-wide `file-size-check` is deliberately unfiltered. Its own
# merge-base diff is the path filter; duplicating its governed roots here is
# the coverage drift this gate is intended to prevent.
# - Deletion-only surface changes do not trigger local hooks: lefthook 2.1.x
# drops deleted paths from push-file discovery (`extractFiles` existence
# check, repository.go). CI's dorny/paths-filter catches deletions.
@@ -51,6 +54,10 @@ pre-push:
commands:
branch-skew:
run: ./scripts/check-branch-skew.sh
file-size-check:
# The ratchet computes its own merge-base diff, so path filtering here
# would only duplicate policy and create another place for coverage drift.
run: just file-size-check
rust-tests:
glob: ["crates/**", "migrations/**", "schema/**", "Cargo.toml", "Cargo.lock", "rust-toolchain.toml", "deny.toml", "scripts/run-tests.sh", "justfile"]
run: just test-unit

View File

@@ -3,10 +3,16 @@ import { promises as fs } from "node:fs";
import path from "node:path";
function git(args, cwd, options = {}) {
// Git hooks export repository-local GIT_* variables. Child commands that
// intentionally target `cwd` must not be redirected back to the hook's repo.
const env = Object.fromEntries(
Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_")),
);
return execFileSync("git", args, {
cwd,
encoding: "utf8",
maxBuffer: 10 * 1024 * 1024,
env,
...options,
});
}

View File

@@ -13,7 +13,17 @@ import {
} from "./check-file-sizes-core.mjs";
function git(repo, ...args) {
return execFileSync("git", args, { cwd: repo, encoding: "utf8" }).trim();
// These fixture repositories inherit both hook configuration and Git's
// repository-local environment when this test runs from pre-push. Isolate
// them completely so fixture commits cannot recurse into the real checkout.
const env = Object.fromEntries(
Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_")),
);
return execFileSync("git", ["-c", "core.hooksPath=/dev/null", ...args], {
cwd: repo,
encoding: "utf8",
env,
}).trim();
}
test("local base resolution uses the branch merge-base and fails without origin/main", () => {

View File

@@ -10,7 +10,7 @@
"check:file-sizes": "node ./scripts/check-file-sizes.mjs",
"check:pubkey-truncation": "node ./scripts/check-pubkey-truncation.mjs",
"lint": "biome lint .",
"check": "biome check . && pnpm check:file-sizes && pnpm check:pubkey-truncation",
"check": "biome check . && pnpm check:pubkey-truncation",
"format": "biome format --write .",
"preview": "vite preview",
"test:e2e": "pnpm build && playwright test",