Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

13 KiB

Local Git Hooks

This document defines the current repository-managed local Git hook behavior.

Purpose

Local hooks should catch cheap, deterministic issues before code leaves a developer machine.

They should:

  • fail early on formatting or boundary violations
  • stay aligned with repository scripts and pull-request checks
  • keep pre-commit light enough for normal daily use

They should not:

  • hide logic in ad hoc shell fragments that cannot be run directly
  • duplicate complex validation flows with different commands than CI

Current Hooks

The repository currently uses husky with two primary hooks:

  • pre-commit
  • pre-push

pre-commit

pre-commit should stay focused on staged-file hygiene and fast checks.

Current behavior:

  • pnpm exec lint-staged
  • pnpm check:backdrop-filter-authoring:staged
  • pnpm check:css-has-performance:staged
  • pnpm check:electron-runtime-boundaries:staged
  • pnpm check:ui-boundaries:staged
  • pnpm check:renderer-boundaries:staged
  • pnpm check:agent-gui-degradation:staged
  • pnpm check:runtime-image-budgets:staged

Rules:

  • staged-file checks should inspect only staged files when practical
  • checks in pre-commit should remain fast and deterministic
  • package-boundary enforcement that is cheap to evaluate may run here
  • TypeScript staged linting should tolerate generated-only changes that are ignored by Oxlint while still failing real diagnostics for linted files

pre-push

pre-push is the changed-aware push-readiness gate before code leaves the machine.

Current behavior:

  • pnpm check:changed -- --push-ready

The hook compares the branch and working tree with the default base ref, selects only the relevant validation lanes, and adds build lanes for changed Go or package surfaces that require push-time build confidence. Unrelated TypeScript, Go, package, and boundary lanes do not run.

Published-package validation is package-scoped. Runtime source, build config, published documentation, and runtime asset changes build the affected public package with its workspace dependencies and inspect only that package's tarball. Root manifests, lockfiles, workspace or Changesets config, package additions/removals, publish-relevant package manifest changes, and release-tool changes retain the full public-package gate. Test files, Vitest support files, and package manifests whose only changes are test scripts do not schedule a package build or tarball check.

check:full remains the stable root command for explicit local full validation. It is no longer the default gate for every push; PR CI selects affected lanes from the same repository check registry used by check:changed.

That root command now uses a repository-owned Node orchestration script so the stable entrypoint stays the same while independent checks can run in parallel in bounded phases:

  • the preparation phase generates builtin app assets once
  • the preflight phase runs generated-artifact and repository-rule checks in parallel
  • the validation phase runs lint, typecheck, and blocking test commands in parallel only after preflight passes

Compact output is the default for both local and automated callers. Each task writes its complete output under .tmp/check-full-runs; successful phases print only timing summaries. A failed task immediately prints up to 120 filtered lines, preserving its assertion, location, and stack context while removing runner boilerplate and repeated consecutive lines. Use pnpm check:full -- --verbose only when live child-process output is needed, or --tail-lines <n> to adjust each failed task's excerpt. The latest machine-readable task results and log paths are recorded in .tmp/check-full-runs/latest.json.

That full validation currently includes:

  • pnpm check:backdrop-filter-authoring
  • pnpm check:css-has-performance
  • pnpm check:defaults-generated
  • pnpm check:agent-gui-provider-catalog-generated
  • pnpm check:api-generated
  • pnpm check:event-protocol-generated
  • pnpm check:workbench-go-contract
  • pnpm check:codexproto-generated
  • pnpm check:tutti-names
  • pnpm check:i18n
  • pnpm check:electron-runtime-boundaries
  • pnpm check:ui-boundaries
  • pnpm check:renderer-boundaries
  • pnpm check:agent-activity-runtime-boundaries
  • pnpm check:agent-host-boundary
  • pnpm check:agent-provider-strategy-boundaries
  • pnpm check:agent-gui-degradation
  • pnpm check:runtime-image-budgets
  • pnpm lint
  • pnpm typecheck
  • pnpm test:ts
  • pnpm test:tools
  • pnpm test:go

The lint and typecheck steps in check:full should follow the repository's documented static-analysis scope: TypeScript linting uses Oxlint and workspace typechecking uses native TypeScript via tsgo.

Rules:

  • pre-push should stay aligned with first-pass pull-request CI through changed-file risk selection
  • slower cross-workspace validation should run only when the changed surface selects it
  • explicit full validation remains available for releases, broad migrations, manual confidence checks, and checked force-push workflows

TypeScript package tests and full Go workspace tests use discovery-based runners instead of root package/module whitelists. Successful runs print compact summaries with the slowest lanes; complete lane logs and a machine-readable latest.json are stored under .tmp/test-runs. Every go.work module participates in explicit full Go gates. The focused agent daemon command and its stabilization expectations are documented in Testing.

For PR branches that often need rebasing or force-pushing, use pnpm push:checked instead of running pnpm check:full and git push manually. It fetches the configured remote branch first and stops before check:full if the remote already has commits not present locally. After check:full passes, it pushes HEAD with an explicit --force-with-lease=<remote-ref>:<fetched-sha> and disables the Husky pre-push hook for that child push to avoid rerunning the same full gate. If another contributor pushes during check:full, the explicit lease still rejects the push. The command requires a clean worktree so the validation run matches the pushed HEAD.

Changed-Aware Validation

The selection and non-duplication policy lives in Testing. This section only defines the changed-aware runner and hook behavior.

The runner selects checks from the changed file set, runs independent lanes concurrently, prints compact summaries, and stores full logs under .tmp/check-runs. Repository policy, tool contracts, generated contracts, and architecture boundaries come from tools/scripts/repository-checks.mjs; PR CI uses the same selectors.

Go package selection is also shared by local changed-aware validation and PR CI. Test module roots come from go.work at runtime; lint uses the repository's established lint-enabled module set. Ordinary Go source changes run the owning package; module manifest changes run the owning module; changes to the workspace, selector, workflow, or shared lint configuration run every test module and lint-enabled module. Tutti daemon lanes validate existing builtin app assets and generate them only when missing or when their source changed.

Lanes that write the same generated artifact are serialized while unrelated lanes remain parallel. In particular, Tutti daemon Go tests and build:go cannot concurrently rewrite builtin app assets, which prevents go:embed from reading a ZIP while it changes.

pnpm check:changed -- --push-ready is the pre-push mode. In addition to the normal changed-aware lanes, it schedules Go or package builds when the changed surface requires them. Package lanes receive the exact public-package subset from the shared change classifier; pull-request CI consumes the same subset.

Failure output prints an 80-line tail by default. Use pnpm check:changed -- --tail-lines <n> when a larger or smaller tail is more useful; full logs remain in .tmp/check-runs.

The runner rejects unknown options, missing option values, and invalid positive integer values. A misspelled --push-ready or malformed concurrency value must fail instead of silently selecting a weaker or empty validation plan.

After failures, prefer pnpm check:changed -- --failed-only to rerun failed lanes after editing the failure. The runner rebuilds the current lane plan and stores a separate input fingerprint for every lane. The fingerprint covers the lane command and its relevant files across the base diff, index, working tree, and untracked contents. A previous result is reused only when that lane passed and its input fingerprint is unchanged. Failed lanes, new lanes, and lanes with changed inputs run again; lanes no longer selected by the current plan are dropped. A failed-only retry inherits the previous run's push-ready mode so failed build and pack lanes cannot disappear from the retry plan. Summaries created before per-lane fingerprints are rejected and require one normal pnpm check:changed run.

When the changed set includes deleted package test files, check:changed should not pass those missing paths to Vitest as explicit targets. Deleting source files should still keep the surrounding package validation active so typecheck can catch broken imports.

When the changed set includes deleted TypeScript or JavaScript files, check:changed should also exclude those missing paths from the lint:changed Oxlint invocation while still preserving the broader changed-file set for boundary checks and package validation.

UI Boundary Enforcement

The shared UI boundary is enforced in two modes:

  • pnpm check:ui-boundaries:staged for pre-commit
  • pnpm check:ui-boundaries when selected by changed-aware pre-push or CI

This keeps commit-time feedback narrow to the staged change while still preserving a full-repository guard later in the flow. The durable details of what the script enforces, including the single allowed non-UI-system workbench stylesheet, belong in the script output and UI System, not duplicated here.

Renderer Feature Boundary Enforcement

Renderer feature internals are enforced in two modes:

  • pnpm check:renderer-boundaries:staged for pre-commit
  • pnpm check:renderer-boundaries when selected by changed-aware pre-push or CI

The script prevents files outside a feature from importing that feature's services/internal/** implementation surface. It also prevents ordinary renderer files from reading window.tutti directly; window container files pass that preload API into feature registrations instead.

Electron Runtime Boundary Enforcement

Electron runtime import boundaries are enforced in two modes:

  • pnpm check:electron-runtime-boundaries:staged for pre-commit
  • pnpm check:electron-runtime-boundaries when selected by changed-aware pre-push or CI

The script walks the runtime import graph reachable from apps/desktop/src/main/** and apps/desktop/src/preload/**. It rejects:

  • React or .tsx modules leaking into Electron runtime execution paths
  • externalized workspace packages that still resolve to raw source files instead of runnable JS

The script also emits fix-oriented suggestions such as using a narrower non-UI subpath or adding the package to the Electron bundling exclude list when that is the intended runtime seam.

Agent GUI Degradation Enforcement

The agent GUI degradation ratchet is enforced in two modes:

  • pnpm check:agent-gui-degradation:staged for pre-commit, blocking new degradation patterns (uncommented timers, swallowed catches, stores created in component files, new provider behavior branches, direct useSyncExternalStore calls, module-level mutable globals, unexplained presentation schedulers/inline compositor hints, unreviewed CSS infinite animations/compositor hints, and transition: all) on staged added lines under packages/agent/gui and packages/agent/activity-core; changes to the AgentGUI stylesheet also preserve the required hidden/inactive pruning rules
  • pnpm check:agent-gui-degradation for check:full, pull-request CI, and a check:changed lane selected when files under packages/agent/ or tools/degradation-baseline/ change; it compares entropy metrics against the committed baseline and fails on any increase, on any decrease that is not locked in by updating the baseline in the same change, or when an exact CSS presentation-hint fingerprint lacks a reviewed lifecycle reason

Details of the metrics and baseline mechanism live in Static Analysis.

Runtime Image Budget Enforcement

Bounded runtime raster assets are enforced in two modes:

  • pnpm check:runtime-image-budgets:staged for pre-commit
  • pnpm check:runtime-image-budgets for check:full, pull-request CI, and the check:changed lane selected by governed runtime assets

The check reads PNG dimensions directly, so it stays fast and does not depend on a platform image tool. Pixel and byte ceilings are documented in Runtime Image Assets.