Skip to the content.

Hook Runtime Bootstrap Model

Decision

The consumer repository hook shim must not own policy behavior, and it must not point at a worktree that can be retired while sibling worktrees still use the same Git common directory.

Its job is limited to:

Policy evaluation, policy freshness, generated prompt packs, runtime command selection, managed capture, diagnostics parsing, and hook behavior belong to the compiled Coding Ethos runtime. The checked-out Coding Ethos authority is the source and build authority; <git-common-dir>/coding-ethos-hooks is an installed, byte-verified projection for durable sibling-worktree execution.

Rationale

Git hook entrypoints live in the Git common directory and are shared by every worktree. Pointing those entrypoints at one authority checkout creates a hidden lifetime dependency: retiring or sandbox-hiding that checkout strands every sibling hook even though the common Git directory remains healthy.

The common runtime is therefore a projection, not a second source of truth:

The invariant is:

the selected checkout builds and verifies authority artifacts.
hooks execute the stable common projection of those artifacts.

If the projection is missing or stale, the error must name the exact supported parent-install or make build command. Lifecycle hooks must not silently select another worktree as authority.

Target Runtime Layout

Authority build artifacts live under the selected coding-ethos checkout and are ignored by Git. The installed hook runtime lives under the consumer repository’s Git common directory:

<authority-checkout>/
  bin/
    coding-ethos-git-hook
    coding-ethos-policy
    coding-ethos-lint
    coding-ethos-agent-hooks
  build/
    policy/
      policy-bundle.json
      policy-metadata.json
    gemini/
      prompt-pack.json
    toolchain/
      manifest.tsv
      go-bin/{golangci-lint,shfmt}
      github-bin/{actionlint,dotenv-linter,hadolint,shellcheck}
      prefix/bin/

<git-common-dir>/coding-ethos-hooks/
  bin/{coding-ethos-*,cerun,git,lint}
  coding-ethos-git-hook
  policy/{policy-bundle.json,policy-metadata.json}
  pre-commit/
  build/{policy,toolchain}/
  {coding_ethos.yml,repo_ethos.yml,config.yaml}

The common projection is runtime state and must never be committed. It is updated only through the supported install/build workflow and validated against the selected authority, rather than edited directly.

Managed Toolchain

Hook execution must not depend on host linters or formatters being installed on PATH. The same checkout-local runtime model applies to third-party tools:

make build is responsible for ensuring required managed tools exist before hook execution. pre-commit/hooks/managed-toolchain.tsv is the checked-in source manifest for required tool versions, release assets, and SHA-256 digests. make managed-toolchain-install installs those tools and writes build/toolchain/manifest.tsv with the installed paths.

coding-ethos-run prepends the managed tool directories to PATH before dispatching to the Go hook runtime. The Go hook runtime also resolves binary tool commands to checkout-local managed paths when possible, so shfmt, shellcheck, actionlint, hadolint, dotenv-linter, and golangci-lint do not silently fall back to host binaries. Missing required managed tools or a missing installed manifest are runtime artifact failures. Runtime commands fail closed and name the explicit make build repair; they do not mutate the installation as a side effect.

Actionlint’s raw JSON-on-stdin ShellCheck dependency protocol has two required inputs: the managed protocol marker and executable identity proving that the immediate parent is the registered managed Actionlint binary. The marker alone is not provenance and is rejected when inherited or supplied by another process.

The managed toolchain has two installer surfaces:

Direct host installs such as go install ... into $HOME/go/bin are not a runtime contract. They may unblock a local shell, but hooks must only rely on artifacts under the coding-ethos checkout.

Build Versus Test Boundary

make build is the explicit environment mutation target. It may regenerate configs, install managed tools, refresh provider settings, install hook entrypoints, compile policy bundles, compile Go runtime binaries, and sync parent hook runtime artifacts.

Test and diagnostic targets must not do those things implicitly. They consume the artifacts produced by make build and fail fast when required artifacts are missing. This prevents ordinary verification commands from rewriting a parent worktree, reinstalling hooks, changing generated config, or performing hidden build setup.

Go tests use the normal Go workflow. make go-test runs go test through managed capture, and make go-e2e-test runs the e2e package with go test. That preserves normalized diagnostics, CEL promotion, trace retention, and SARIF-compatible output without introducing a separate compile-and-run test path.

Hook Entrypoint Contract

The installed consumer repository hook entrypoint is a small executable script. It passes the hook kind and hook name explicitly, for example <git-common-dir>/coding-ethos-hooks/bin/coding-ethos-run git-hook pre-commit "$@", so installed Git hooks do not rely on argv[0] inference or a worktree-local path.

The supported install/check workflow owns the bootstrap contract:

  1. Resolve the consumer repository and its absolute Git common directory.
  2. Build Go tools from the explicitly selected Coding Ethos authority.
  3. Atomically install the compiled executables into the common runtime.
  4. Install the remaining policy, toolchain, and hook artifacts through make build when the full runtime is being refreshed.
  5. Verify executable type, mode, and byte identity with:

    coding-ethos/bin/coding-ethos-run parent-check --repo "$consumer_root"
    
  6. Install Git entrypoints that dispatch only to the common runner.

The hook entrypoint contract must not:

Repair Rules

Bootstrap repair is explicit. parent-install refreshes generated parent artifacts and compiled common-runtime executables; make build refreshes the complete projection. It should not run because a timestamp looks old.

Examples that require repair:

Examples that should not block lifecycle hooks:

Strict freshness validation belongs in explicit maintainer/CI commands such as make validate, make cutover-verify, and CI. Freshness is based on the source hashes recorded in build/policy/policy-metadata.json, not mtimes.

Executable Projection Maintenance Contract

The compiled Go parent-runtime-sync command is the only installer for the shared Go executable projection. Its inventory is every buildable command under go/cmd/, plus the root-level compatibility coding-ethos-git-hook. The Makefile delegates to that command and does not copy those executables itself.

Within the shared runtime, the coding-ethos-* filename prefix is reserved for managed executables. Sync removes obsolete regular files in that namespace, preserves unrelated filenames, and fails without removing an obsolete entry whose shape is a directory, symlink, or special file. Expected destinations must also be regular files or absent.

Each executable is written to a uniquely named temporary file in its destination directory, assigned the authority artifact’s mode, flushed, and activated with an atomic rename. Temporary files are removed on every return. Concurrent syncs therefore converge on one complete authority artifact rather than exposing a partial copy. parent-check verifies regular-file type, exact mode, SHA-256 content, compatibility-hook presence, and absence of stale managed entries.

Both the authority checkout’s resolved hook directory and any consumer hook directory are shared Git state. Installed entrypoints always name <git-common-dir>/coding-ethos-hooks/bin/coding-ethos-run; no hook installation may embed the selected worktree’s bin/coding-ethos-run path.

Safety Requirements

Bootstrap needs a few guardrails:

Hook Execution Model

Hook execution follows a three-phase model designed for maximum parallelism while preserving correctness ordering.

Phase 1 — Format (Sequential Gate, Per-Language Parallel)

Formatters mutate files and must complete before linters run. Within the format phase, per-language formatter chains run concurrently:

The two lanes operate on disjoint file sets and run in parallel goroutines. A cross-language text fixer (fixText) runs first and gates both lanes.

If any formatter fails or the format phase produces a non-zero exit, the hook stops immediately.

Phase 2 — Analysis Groups (Fully Parallel)

All non-AI analysis groups run concurrently as independent goroutines:

Within a group, commands run sequentially by default. Groups may declare a ParallelAfter index to split their command list into:

For example, the go group uses ParallelAfter: 2:

Index Command Phase
0 go-format Sequential
1 go-vet Sequential
2 go-test Parallel
3 go-coverage Parallel
4 golangci-lint Parallel

If any sequential prefix command fails, the parallel suffix is skipped for that group.

Phase 3 — AI (Gated)

AI review groups (e.g., gemini-check) run only after all Phase 2 groups succeed. This avoids wasting API credits on code that has already failed deterministic quality gates.

Incremental Linting

During pre-commit, golangci-lint receives --new-from-rev=HEAD so it only reports issues in changed code. During pre-push, it runs on all files for complete coverage.

Migration Direction

Runtime artifacts are built from an explicitly selected coding-ethos authority and executed from the stable common Git projection. New hook behavior must preserve that one-way authority-to-projection relationship: no hook may select an arbitrary sibling checkout, and no installed executable may link back to a checkout that can be retired.