HOVEL // chapter 30
Chapter 30 Part 05 / Engineering

// repository workflow

Development Guide

Hovel is a Bazel monorepo operated through Aspect CLI. The checked-in .aspect/ commands compose pinned toolchains and declared targets so contributors and CI use the same build graph.

Use Aspect CLI as the only build entry point. Do not invoke Bazel, gofmt, uv, rustfmt, clang-format, or Lefthook directly. Add or repair an Aspect command or Bazel target when a workflow is missing.

Everyday commands

CommandPurpose
aspect hovel-checkRun the complete non-publishing gate for repository policy, core, SDKs, example modules, modules, docs, and agent integrations.
aspect hovel-check <scope>Check one of repo, core, sdk, module-examples, modules, docs, or agent.
aspect hovel-formatFormat the wired Go, Python, Rust, and Squatter sources and refresh Gazelle metadata.
aspect hovel-reportRun report-producing tests and materialize the evidence-backed static site.
aspect hovel-release <kind>Build, without publishing, hovel, sdk, modules, picblobs, picblobs-cli, or agent artifacts.
aspect hovel <mode> -- …Run the development CLI, daemon, MCP adapter, status, initialization, throw, or session workflow.
aspect build|test|run …Address a declared Bazel target through the repository's selected configuration.

Repository slices

PathOwnsFocused gate
core/Daemon, front ends, domain, application services, storage, and core build tooling.aspect hovel-check core
sdk/Public Go, Python, and Rust module authoring contracts.aspect hovel-check sdk
modules/examples/Cross-language compatibility examples.aspect hovel-check module-examples
modules/Integrated modules, including Squatter and Picblobs.aspect hovel-check modules
docs/Book content, module manuals, generated API references, demos, and the static site.aspect hovel-check docs
agent/Canonical Agent Skills and Claude, Codex, and OpenCode distribution packaging.aspect hovel-check agent

These are ownership boundaries, not permission to bypass the root build graph. Cross-slice dependencies should use narrow public labels and public SDK or wire contracts rather than importing core internals.

Hermetic tool ownership

Bazel owns build, lint, test, documentation, and demo tools whenever practical: pinned archives, Python wheels, and Go, Rust, C, and cross-compilation toolchains. Host services are reserved for real system boundaries such as Docker, Wine, tmux, ttyd, or ffmpeg. Repository tooling that materializes generated output is a declared Bazel-run program with explicit data dependencies.

BuildBuddy and CI

Select buildbuddy in the ignored .hovel-bazel-config file to use the organization's remote cache, execution service, and build event UI. Both workspaces declare the same digest-pinned Ubuntu worker image with action networking disabled. Language runtimes, compilers, and compiler support libraries are inputs in the Bazel graph; the worker image supplies the OS.

Keep the API key in ~/.config/hovel/buildbuddy.bazelrc with mode 0600. Its only setting is build:buildbuddy --remote_header=x-buildbuddy-api-key=YOUR_KEY. Import that file by its absolute path from both ignored .bazelrc.user and core/.bazelrc.user using try-import /absolute/path/to/buildbuddy.bazelrc. Credentials never belong in tracked rc files or command-line arguments.

CI dependency caches are isolated by check scope and release family, with toolchain definitions included in their keys. Job summaries show complete Aspect task times, including host executables; nested task times overlap. To inspect a completed run, use aspect run //repo-tools/tasks:ci_audit -- --run-id=RUN_ID --credential-file=/path/to/private/buildbuddy.bazelrc --output=/path/to/audit.json. The audit uses GitHub authentication from the environment or GitHub CLI and records only selected timings and cache counters. Logical and transferred bytes include repeated worker reads, not just downloads to the CI runner.

Aspect adds invocation metadata following the BuildBuddy metadata guide: repository, checked-out commit, branch, and worktree state. GitHub runs also carry the actor, CI role, workflow, job or matrix scope, run attempt, event, and logical Aspect step. BuildBuddy links lead back to the exact GitHub Actions attempt, source commit, and pull request. The commit describes the actual checkout, including GitHub's PR merge commit, rather than assuming that the event SHA is the source built by a manually selected release. These are invocation fields; changing them does not stamp or invalidate compiled artifacts. Indexed tags are limited to project, worktree state, and a bounded job name so long command lines cannot exceed BuildBuddy's tag storage limit.

Set the GitHub Actions secret BUILDBUDDY_API_KEY to enable remote execution in CI and release jobs. The shared setup action invokes aspect hovel-ci setup, writes a mode-0600 credential rc under RUNNER_TEMP, and exports its path. Without the secret, fork PRs and Dependabot builds use local execution while retaining the same Aspect gates. CI metadata configuration alone does not enable a remote service. The setup action disables setup-aspect's additional disk and repository caches: BuildBuddy owns action caching, and the shared GitHub cache stores only dependency downloads. The Aspect launcher and CLI are pinned together.

Each job finishes with a table of BuildBuddy invocations and their results, including incomplete invocations from failed builds, then removes its credential file. Summaries read only allowlisted fields from local build events; raw event files and rc files are never uploaded as artifacts. Results retain BuildBuddy's authenticated visibility and default environment redaction. GitHub job checks aggregate the Aspect workflow; per-invocation BuildBuddy commit statuses are disabled to avoid a competing set of checks. Aspect's automatic GitHub comments, check runs, and artifact uploads are also disabled; the explicit workflow steps own those surfaces without an Aspect API token or extra CI write permissions.

CI uses a matrix for the repository, core, SDK, example, module, and agent scopes, with separate evidence-backed docs and Wine jobs. The scopes keep their stable check names, continue independently on failure, and have explicit timeouts. Build artifacts are retained for 14 days and failed test logs for seven days. Wine and Docker are host-service boundaries; aspect hovel-ci wine keeps Wine tests on the runner while compilation uses remote workers. The report workflow downloads remote test outputs for the assembled evidence-backed site. Pages promotes the exact artifact from a successful main push; only a manual Pages dispatch rebuilds it. OIDC and write permissions belong to publishing jobs.

Runner bootstrap and summary programs use only the host Python standard library through aspect hovel-ci: they must work before Bazel authentication exists and after a build fails. Their regression tests and the pinned Actionlint binary run through aspect hovel-check repo.

Definition of done

  1. Run the focused scope while iterating.
  2. Run aspect hovel-format after source or import changes.
  3. Run aspect hovel-check before considering a full-repository change complete.
  4. For public SDK or module protocol changes, run aspect hovel-report and inspect the compatibility and branch-coverage evidence.
  5. For docs-only changes, at minimum run aspect hovel-check docs; use the report command when the materialized site must contain current evidence.

Design boundaries

Core follows inward dependencies: adapters and infrastructure call application services, which call the pure domain. Front ends do not reach around application services, and modules depend on public descriptors rather than internal packages. Preserve defensive copies and constructor validation at boundaries. Changes to throw planning, confirmation, dangerous-module checks, or audit events must retain the safety rules in Safety and Guardrails.

Release procedure

Hovel releases are driven by an annotated v* tag whose version matches the committed VERSION. The release workflow accepts the tag only when its commit is contained in main, runs the complete non-publishing gate, builds every artifact through Aspect, publishes Python distributions with trusted publishing, and creates the GitHub Release last.

aspect run //core/tools/release:upver -- 0.4.0
aspect hovel-format
aspect hovel-check

# After merge and a successful build-only workflow dispatch on main:
git tag -a v0.4.0 -m "Hovel v0.4.0"
git push origin v0.4.0

Do not manually create the GitHub Release first. Rerun the same tag workflow after a transient failure; publication verifies existing PyPI filenames and hashes rather than replacing immutable files. Hovel and hovel-sdk share the Hovel version. Picblobs and picblobs-cli retain their own synchronized version and are published change-aware as part of the release train.

PyPI trusted publishers must name release.yml. Hovel projects use the pypi environment; Picblobs projects use pypi-picblobs and pypi-picblobs-cli. Only publishing jobs receive OIDC permission, and only the terminal GitHub Release job receives repository write permission.