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

279 lines
13 KiB
Markdown

# 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](./testing.md).
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](./testing.md#validation-selection). 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](../../packages/ui/system/ui-system.md), 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](static-analysis.md).
## 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](runtime-image-assets.md).