Fuchsia developer workflows span multiple Git repositories (//, //vendor/*, //integration) coordinated by Jiri. Ensuring that code committed locally meets platform formatting, documentation, and commit message standards is essential to preventing presubmit and CQ cycle failures.
The Git staging and hook subsystem runs pre-commit and commit-msg checks without clobbering unstaged edits:
HEAD.fx format-code formats fully staged files with automatic re-staging (git add). Partially staged files are evaluated strictly in read-only check mode under stash isolation.Bug:, Test:, Change-Id:) via scripts/shac/commit_msg_checker.py.//integration) distributes a hook dispatcher (hook-dispatcher.sh) and root hook wrappers (pre-commit, commit-msg). fx agents setup drops scripts into .git/hooks/<hook_name>.d/ without modifying the root wrappers.In Git worktrees (git worktree add), .git is a file pointing to the main repository, causing relative path lookups (.git/hooks/...) to fail. Furthermore, developers or other tools may have custom scripts they wish to run during pre-commit or commit-msg phases. Overwriting these scripts leads to broken workflows.
<hook>.d/)Jiri (via //integration) natively distributes the universal dispatcher (hook-dispatcher.sh) and root hook wrappers (pre-commit, commit-msg).
.git/hooks/pre-commit (Jiri Distributed Wrapper)
│
└──► .git/hooks/pre-commit.d/
├── 05-author-check.sh (Upstream Googler email check)
├── 10-fuchsia-agent.sh (Automated formatting & staging isolation)
├── 10-unstage-submodules.sh (Submodule reset check)
└── 50-team-tool.sh (Modular team/user scripts)
The dispatcher is handled natively by Jiri executing directory-based multi-hooks (<hook_name>.d/*) in lexical order. Check //integration/git-hooks/hook-dispatcher.sh for the dispatcher implementation.
fx agents setup):git rev-parse --git-path hooks (ensuring compatibility with Git worktrees and subdirectories).install_git_hooks creates .git/hooks/<hook_name>.d/ and writes or updates 10-fuchsia-agent.sh..git/hooks/<hook_name>.fx agents setup --reset):uninstall_git_hooks to delete only .git/hooks/<hook_name>.d/10-fuchsia-agent.sh..git/hooks/<hook_name> wrappers and <hook_name>.d/ directory intact.<hook_name>.d/ continue executing without disruption.get_git_hooks_status.The Git staging and hook subsystem is split into three layers:
lib/git_staging/):StashGuard), and pipeline execution.git diff, git apply, git status) with no Fuchsia-specific dependencies.lib/githooks/):fx format-code, commit_msg_checker.py).ConsoleReporter) and the hook CLI dispatcher (runner.py).commands/setup.py, lib/githooks/installer.py):// and //vendor/*.10-fuchsia-agent.sh within .git/hooks/<hook_name>.d/.For the complete repository and test file layout, see tools/agents/DEVELOPMENT.md#directory-layout.
The 10-fuchsia-agent.sh hook fragment exports PYTHONPATH="$FUCHSIA_DIR/tools:$PYTHONPATH" and executes $FUCHSIA_DIR/scripts/fuchsia-vendored-python. This allows hooks to run during git commit without depending on Ninja/GN build artifacts or an active build directory.
ConsoleReporter formats messages based on terminal capabilities:
🧹, ⚠️, ❌) on UTF-8 interactive terminals.[FIXED], [WARN], [ERROR]) on non-UTF-8 terminals or plain consoles. Clean commits run silently.❌ Formatting issues in partially staged files. Auto-fix skipped to prevent stash conflicts.
Offending files:
- src/sys/pkg/lib/fuchsia-pkg/src/meta.rs
Or fix directly:
fx format-code --files=src/sys/pkg/lib/fuchsia-pkg/src/meta.rs
The subsystem uses frozen dataclasses and typed callables for pipeline state:
ActionContext: Generic execution context passed to staged pipelines (repo_root, check_only).HookContext: Specializes ActionContext for Fuchsia hooks with is_agent flag and ConsoleReporter.PipelineResult: Immutable result of pipeline actions, tracking success, restaged_files, partial_conflicts, and errors.StagedPartition: Immutable partitioning of staged files into fully_staged and partially_staged frozensets.HookAction: Specification of an action in the hook pipeline, defining name, mutate capability, formattable extensions filter, and failure remediation callbacks.HookOperationResult & HookStatusResult: Structured summaries of multi-repo hook installation and status queries.See tools/agents/lib/git_staging/engine.py and tools/agents/lib/githooks/adapters.py for the complete implementations.
StashGuard)The StashGuard context manager handles temporary stash isolation:
__enter__): Detects unstaged working tree modifications via git status --porcelain and pushes them to a temporary stash with git stash push --keep-index -q -m git-staging-isolation.__exit__): Captures the binary stash diff between the index commit and stash commit ($S^2 \to S$) via git diff --no-color --binary <stash>^2 <stash>, reapplies it via git apply --binary --allow-empty, and safely drops the stash commit on successful restoration.git stash push --keep-index omits unstaged deletions of newly added index files from the stash diff. StashGuard explicitly tracks and restores these deletions (_unstaged_deleted_files) to prevent phantom files or index corruption.raise GitError(...) from exc) to preserve tracebacks.See tools/agents/lib/git_staging/engine.py for the complete implementation.
GitError(RuntimeError): Core exception raised on Git command failures, repository resolution issues, or stash isolation errors. Ensures clean error wrapping across CLI entrypoints and runners.Hooks can be bypassed using standard Git flags, environment variables, or fx agents setup (see tools/agents/README.md#git-hooks-integration):
git commit -n or git commit --no-verify (standard Git flag, skips pre-commit and commit-msg natively).FUCHSIA_SKIP_HOOKS=1 git commit ... (immediately exits 0 from the hook runner).fx agents setup installs .git/hooks/<hook_name>.d/10-fuchsia-agent.sh.fx agents setup --status: Displays current configuration and reports Git hook configuration across all checkout repositories.--git-hooks / --no-git-hooks: CLI flags for fx agents setup controlling Git hook installation across checkout repositories (default: --git-hooks). Passing --no-git-hooks configures agent permissions and profiles without installing or modifying Git hooks.fx agents setup --reset removes 10-fuchsia-agent.sh while leaving dispatcher and user scripts intact.fx agents setup --reset --no-git-hooks: Resets Fuchsia-managed rules and configuration while preserving existing Git hooks intact (skips hook uninstallation).pre-commit or commit-msg hooks when automatically applying commits during a rebase.git rebase -i) to edit, amend (git commit --amend), or insert a commit, Git executes the hooks normally. The staging engine ensures newly amended or inserted commits are cleanly formatted and verified without false merge conflicts or unintended hook bypasses.Running fx agents setup configures the checkout, but hooks only apply agent-specific behavior when an agent is actually executing:
~/.local/share/Fuchsia/agents/setup/state.json indicates that fx agents setup configured the developer checkout, but does not mean an automated AI agent is executing a given commit.git commit commands in repositories configured with fx agents setup. These interactive human commits receive human-oriented UX (ConsoleReporter, advisory warnings, colored summaries).Runtime AI agent execution is detected dynamically at commit time using canonical environment variables:
is_invoked_by_agent(), which checks the canonical agent environment variables defined in AGENT_ENV_VARS (ANTIGRAVITY_AGENT, GEMINI_CLI, ANTIGRAVITY_EDITOR_APP_ROOT).When active runtime agent execution is detected:
commit_msg_checker.py --strict so non-compliant messages are rejected before reaching CQ.| Failure Mode | Policy | Action | Rationale |
|---|---|---|---|
| Missing Tool / Python Environment | Fail-Open | Print warning to stderr; exit 0. | Never block commits due to environment setup issues. |
| Stash Restoration Failure | Fail-Closed | Print recovery command; exit 1. | Prevent silent data loss or corrupted working trees. |
| Code Syntax / Formatter Error | Fail-Closed | Print formatter error; exit 1. | Catch broken code before creating commits. |
| Partially Staged Formatting Error | Fail-Closed | Print remediation hint; exit 1. | Avoid committing unformatted code or corrupting unstaged hunks. |
| Commit Message Warnings | Fail-Open (Human) Fail-Closed (Agent) | Advisory warning for humans; error for agents. | Warn human developers without blocking commits; block commits from automated agents. |
fx format-code entirely if the staged partition contains zero formattable files. Supported extensions match DEFAULT_FORMATTABLE_EXTENSIONS (.py, .md, .c, .cc, .cpp, .h, .hh, .hpp, .rs, .go, .fidl, .gn, .gni, .json, .json5, .cml, .proto, .ts).git diff --binary and git apply --binary preserve binary content without corruption, while staged path filtering targets source file extensions directly.When a file contains both staged and unstaged hunks, mutating formatters cannot safely rewrite the file on disk without corrupting the unstaged hunks. If formatting issues are detected in partially staged files, the commit is blocked with instructions on how to fix it.
Developers have three standard remediation paths:
git add <file> && git commit
git checkout -- <file> && git commit
fx format-code --files=<file> && git add <file> && git commit
In the rare event that working tree restoration encounters an unexpected conflict or failure (e.g. external process modification during commit), StashGuard aborts and leaves the temporary stash at stash@{0}. The developer can inspect and recover the working tree using:
git stash list git stash apply stash@{0}
Hooks can be tested directly without creating a Git commit:
# Run pre-commit hook checks on current index: $FUCHSIA_DIR/scripts/fuchsia-vendored-python tools/agents/lib/githooks/runner.py pre-commit # Test commit-msg hook with a sample message file: $FUCHSIA_DIR/scripts/fuchsia-vendored-python tools/agents/lib/githooks/runner.py commit-msg path/to/COMMIT_EDITMSG # Simulate AI agent invocation: ANTIGRAVITY_AGENT=1 $FUCHSIA_DIR/scripts/fuchsia-vendored-python tools/agents/lib/githooks/runner.py pre-commit