Documentation Dalo in CI

Using Dalo in CI

Dalo's JSON output and exit codes are intended for automation. A pipeline can check that a skill repository still resolves, audits, and links cleanly before a change reaches anyone's agent.

Useful checks:

Passive release checks are disabled automatically when the conventional CI environment variable is set. DALO_OFFLINE=1 or DALO_UPDATE_CHECK=never can also disable them explicitly in other managed environments.

Example GitHub Actions job

name: dalo

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  dalo:
    runs-on: ubuntu-latest
    env:
      DALO_STORE: ${{ runner.temp }}/dalo-store
    steps:
      - uses: actions/checkout@v7

      - name: Install dalo
        run: cargo install dalo

      - name: Configure a temporary Dalo store
        run: |
          dalo init
          dalo target link generic "$RUNNER_TEMP/dalo-skills"
          dalo source add project .
          dalo sync

      - name: Check dalo status
        run: dalo status --check --json > dalo-status.json

      - name: Check dalo health
        run: dalo doctor --check --json > dalo-doctor.json

The checkout in this example is a local Git source. Replace . with the path or URL of the skill repository that the workflow should validate. The temporary store and generic target keep the check isolated from any runner state.

Exit codes

Code Meaning
0 Success
1 Expected actionable failure, including semantic value validation, failed checks, and security-audit blocks
2 Usage error from invalid arguments or flags; emitted as plain text even with --json
3 Unsafe state blocked the operation
4 Dependency or environment problem

Treat 1 as a user-actionable configuration or drift problem, 3 as a safety stop that should not be auto-fixed, and 4 as a runner/tooling problem.

Catalog drift

For catalog sources, use the read-only refresh check. --check changes only the exit status; neither form advances the catalog pin:

dalo source refresh company-catalog --check

The command reports new available skills, selected skill changes, moved or removed selections, and changed requirements without changing the source lock. New unselected skills remain informational; any selected-skill drift exits with code 1 for review.

Pin advancement is deliberately separate from CI checking. Preview the exact transaction without writes, then apply it only in a reviewed maintenance flow:

dalo --dry-run --json source refresh company-catalog --advance
dalo source refresh company-catalog --advance

The advance report contains both lock entries, every drift classification, the affected materialization plan, and blocking reasons. Never add --advance to an unattended drift-check job.

How Dalo itself is released

The rest of this page is for maintainers. It documents how a Dalo release is built and published, and you do not need it to run Dalo in your own pipeline.

Release publication

The publish workflow keeps a new GitHub release as a draft while its five target archives, checksums, and Sigstore bundles build and upload. The workflow creates the release tag at draft time so each matrix job can check out the exact release commit. A single final job verifies every expected asset and publishes the release only after the complete matrix succeeds. If a build is failed or cancelled, the incomplete release remains a non-public draft and the preceding public latest release stays available.

GitHub is published before crates.io, npm, or the Homebrew tap dispatch. Each of those downstream channels depends on the final GitHub-release job, so no public installer path advertises an archive before GitHub makes that archive available.

Release candidate rehearsal

The repeatable part of the release rehearsal runs against a clean temporary home, store, and generic agent target. It exercises the documented initialize, target, local-skill, sync, status, doctor, plan, and recovery-summary paths, plus assistant installation, repeat installation, target delivery, and preservation of a user-edited assistant. It prints a transcript for the CI log. This checks CLI behavior; it does not prove that a real agent discovers and invokes the skill.

sh scripts/release-rehearsal.sh

CI runs the same script on its Linux and macOS jobs using the release binary already built for that runner. When a 1.0.0 candidate is available, set DALO_REHEARSAL_EXPECTED_VERSION=1.0.0 so the rehearsal also verifies the candidate version. This does not replace the post-tag distribution smoke test: the five public channels below still have to be checked from clean machines after GitHub publishes the release.

Releasing 1.0.0

release-please-config.json sets bump-minor-pre-major: true, so every feat!: commit below 1.0.0 produces a minor bump, not a major. The first major is requested explicitly, with a Release-As: footer on a commit that reaches main:

chore(release): release dalo 1.0.0

Release-As: 1.0.0

The footer is matched case-insensitively on the token Release-As, it must sit in the commit footer, and it works with any Conventional Commit type — including types the changelog normally hides, because the changelog generator keeps a commit that carries the footer. Pull requests are rebase-merged, which preserves the footer; a squash merge only preserves it when the squash body keeps it. Other trailers such as Co-Authored-By: may follow it.

Release-please then opens chore(main): release dalo 1.0.0 on release-please--branches--main--components--dalo, writes the ## [1.0.0] CHANGELOG heading, and stamps 1.0.0 into Cargo.toml, Cargo.lock, npm/package.json, both npm/package-lock.json version paths, and the three annotated slots in site/index.html (softwareVersion plus the two data-dalo-version spans). Release Please generates the GitHub release notes and CHANGELOG from Conventional Commits.

Merging that release pull request tags dalo-v1.0.0 and runs publish.yml: five signed archives and signed amd64/arm64 Debian packages upload with checksums to a draft release, the draft is published once every asset is present, and crates.io, npm, and the sebastian-software/homebrew-tap dispatch follow. The tap bump compares versions with sort -V, so 1.0.0 supersedes 0.16.0 rather than losing to it in string order.

Smoke every channel once the release page is public:

brew upgrade dalo && dalo --version
npx getdalo@latest --version
cargo binstall dalo && dalo --version
mise up && dalo --version
curl -fsSL https://dalo.sh/install.sh | sh && dalo --version

Then check the upgrade path itself from a 0.16 install: run any command under a TTY with DALO_UPDATE_CHECK unset and CI unset, and confirm the notice reads update available: dalo v1.0.0 (installed v0.16.0 via <channel>) with one v per version and the upgrade command that matches the channel it was installed from.

Final 1.0 acceptance

Keep evidence on #841 for the exact candidate commit and the published tag. Closed implementation issues do not stand in for these checks:

Source: docs/ci.md