Thanks for contributing. This file documents the conventions and tooling that keep CLEO's commit history machine-readable for orchestration and post-release reconciliation.
pnpm install # install workspace deps
pnpm run hooks:install # wire up git hooks (simple-git-hooks)
pnpm run build # build all packages
pnpm run test # run the full vitest suite
pnpm biome check . # lint + format checkCommits MUST follow the conventional-commits format that the existing
.git/hooks/commit-msg enforces:
<type>(<scope>): <subject>
Allowed types: feat, fix, docs, style, refactor, test, chore.
scripts/hooks/commit-msg-release-lint.mjs enforces an additional rule on
release commits — those whose subject begins with chore(release): or
feat(release):.
Every release commit MUST cite at least one
T<digit>+task ID in the body.
The task ID anchors post-release reconciliation (T1411): the reconciler reads
the release commit, extracts the cited task IDs, and stamps them done in
.cleo/tasks.db. Releases that ship work without naming the tasks they ship
break that pipeline.
Add a Refs: line (or any line containing T\d+) to the commit body:
chore(release): v2026.4.145
Refs: T1407, T1410, T1411
- Non-release commits: hook exits
0immediately (perf-fast path). - Release commit with one or more
T\d+references: exit0. - Release commit without any
T\d+reference: exit1with an actionable error message.
For genuine emergencies — incident hotfixes where an operator must ship before a task can be filed — the hook supports an audited bypass:
CLEO_OWNER_OVERRIDE=1 \
CLEO_OWNER_OVERRIDE_REASON="incident NNNN hotfix" \
git commit -m "chore(release): v2026.4.146"Both env vars are required. The bypass appends one JSON-line record to
.cleo/audit/force-bypass.jsonl capturing the hook name, ISO-8601 timestamp,
the operator-supplied reason, and the offending commit subject. Use this
sparingly; every bypass is auditable.
CLEO uses simple-git-hooks
for hook wiring (no postinstall scripts, no autoinstall). The hook config lives
under the simple-git-hooks key of the root package.json. After pnpm install you MUST run:
pnpm run hooks:install…once to register the hooks with git. Re-run it whenever the hook config changes.
To skip hook setup in CI or for an unsupported environment, simply do not run
hooks:install — git will fall back to its built-in hooks (or none).
scripts/hooks/post-tag.sh runs the registry-driven post-release invariants
gate (T1411 / ADR-056 D5) for a release tag. It invokes
cleo reconcile release --tag <tag>, which:
- Reads the tag annotation and every commit between the previous tag and the target tag.
- Extracts every
T\d+task ID from those messages. - For each task ID:
- If verification gates have all passed, stamp
status='done',archive_reason='verified', andrelease='<tag>'. - If verification is null or incomplete, create a follow-up task
T-RECONCILE-FOLLOWUP-<tag>-<idx>linked to the original.
- If verification gates have all passed, stamp
- Appends every mutation to
.cleo/audit/reconcile.jsonl.
Git does not provide a native post-tag hook (the only tag-related hook is
pre-push, which fires before a push), so this script is invoked manually
or by CI runners that detect newly-created tags. Recommended patterns:
# Manual invocation immediately after `git tag`
scripts/hooks/post-tag.sh v2026.4.145
# CI runner that fans out from a tag-trigger event
- run: scripts/hooks/post-tag.sh "${{ github.ref_name }}"The hook forwards the CLI exit code:
0— clean reconcile, no follow-ups.1— at least one invariant raised an error (operator MUST investigate).2— one or more unreconciled tasks; follow-up tasks were created.
cleo reconcile release --tag v2026.4.145 --dry-runDry-run mode reads the tag and commit range, extracts task IDs, and prints
what would happen, but writes nothing to tasks.db or
.cleo/audit/reconcile.jsonl. Useful for validating the cited task list
before tagging.
cleo reconcile release --tag v2026.4.145 --jsonEmits the raw aggregated InvariantReport for downstream tooling (release
gates, dashboards).