Skip to the content.

Continuous integration

AncestryLLM uses progressively broader gates so routine code generation gets fast feedback while expensive cross-platform and security checks still run before changes can be released.

Local development

Run targeted tests while editing. make bootstrap installs two hook tiers:

  • the commit hook retains the generic file-safety checks and local system gitleaks scan, then uses exact-commit astral-sh/ruff-pre-commit hooks for Ruff check and format-check behavior and exact-commit astral-sh/uv-pre-commit for uv lock --check; the Ruff hooks do not apply fixes;
  • the pre-push hook runs make pre-push, which expands to the canonical test, lint, type-check, dependency-audit, and Semgrep gates. It also runs make workflow-audit when a pushed commit changes .github/workflows/ or .github/actions/.

The exact hook commits match the lock-resolved Ruff 0.16.1 and repository uv 0.12.1 versions. CI and Make remain authoritative: make lint also applies the checked-in GFM structural checks to every tracked Markdown file, whether or not a contributor installed the hooks.

make setup first runs the verified uv bootstrap, then uses the repository-local .tools/uv/uv binary to install the locked environment without changing Git hooks. It requires a system-supplied Python 3.12-3.14; .python-version selects 3.12 by default, and [tool.uv] disables Python downloads. Setup runs the exact full-profile command uv sync --locked --all-extras --all-groups. The bootstrap refuses an unverified uv from PATH, re-hashes a cached local binary, and emits the sanitized .tools/receipts/uv-bootstrap.json receipt before uv --version or another uv command may run. This is the appropriate target for automation and disposable environments. Local developers authenticate once with gh auth login --hostname github.com; headless shells provide GH_TOKEN through their secret manager. The bootstrap uses that credential only with the policy-pinned, hash-verified GitHub CLI and never delegates verification to an executable found on PATH.

Locked environment profiles

The complete uv.lock covers every application extra and repository tool group. Full local setup installs that complete graph, including the release verifier. Purpose-specific workflow jobs pass --no-default-groups and synchronize only the profile they execute. The lock consistency job installs no group and calls make lock-check, whose canonical command is uv lock --check.

Work Locked environment
Local full setup All application extras and every dependency group, including release-verifier
Python test matrix test plus all-llm
Quality lint plus typecheck; exact ty is installed only for its visible advisory step
Dependency audit, SBOM, and workflow audit security; the pinned Semgrep script remains independent
Package and release construction build; release construction also installs security only for SBOM generation
Production PyPI artifact verification release-verifier only
Native desktop sidecar packaging desktop-build only
Release-project proof test only

No quality, security, or build job installs provider extras. After any allowed narrow synchronization, workflow jobs call the same canonical Make target used locally; they do not restate or vary its command arguments. The Python 3.12 release-readiness row first exercises the provider-aware test profile, then replaces it with lint and typecheck before static checks; the separate security-evidence job installs security. This prevents a successful gate from depending on packages left behind by another profile. The exact profiles and maintenance procedure are documented in Dependency maintenance.

The Python 3.12 quality job keeps make typecheck as the blocking strict-mypy gate. A separate make typecheck-ty step runs exact ty 0.0.69 over src/ancestryllm with continue-on-error: true; it does not use || true, so the Actions UI retains ty’s actual status. Release readiness and release evidence continue to require the schema-v1 mypy result. The counts, parity fixtures, Pydantic limitations, timings, and cutover disposition are recorded in the ty advisory evaluation. Its authoritative 58-diagnostic count uses this provider-free quality profile; a documented all-extras setup resolves five optional provider imports without satisfying the cutover gate.

Ruff remains lock-resolved at 0.16.1. Quality jobs select GitHub annotation output through RUFF_OUTPUT_FORMAT=github and still invoke the canonical make lint target without restating its flags. The enabled rule families, reviewed diagnostic batches, provider-import contract, and cold-start evidence are recorded in the Ruff rule-expansion evaluation.

Production package and release jobs continue to call make package with setuptools as the authoritative backend. make evaluate-uv-build is a maintainer-only, locked-build-group comparison: it builds the same clean commit with setuptools and uv_build, records schema-v1 artifact and semantic evidence, and exits nonzero when the candidate differs. It is deliberately not a CI or release gate because the 0.6 evaluation is incompatible; see the uv_build evaluation. A future adoption change must first satisfy that complete artifact contract in a separate 0.7 decision.

Headless shell policy

Every workflow that executes a command sets the workflow-level default shell to noninteractive Bash. Individual steps may override that default only when the host requires another native shell: the Windows signing, host-inspection, installer-validation, and cleanup steps use PowerShell because they call Windows APIs or PowerShell-only signing tools. No CI workflow or Make recipe uses zsh or an interactive shell profile.

The Makefile pins its recipe shell to /bin/bash, so local and CI invocations have the same command semantics regardless of the caller’s interactive shell.

Hosted gate tiers

Event Required work
Pull request An early make lock-check gate; tests on Python 3.12; one Python 3.12 quality job; Semgrep; a commit-range secret scan; package build; Ubuntu/Python 3.12 wheel and source-distribution smoke tests. Dependency audit and SBOM generation run only when pyproject.toml or uv.lock changes. Workflow auditing runs when a workflow or local composite action changes.
Push to main The pull-request coverage plus all nine Ubuntu/macOS/Windows and Python 3.12-3.14 wheel-install combinations, dependency audit, SBOM generation, and workflow auditing.
Weekly schedule or manual dispatch The complete main gate set. The secret scanner checks the current main candidate tree from a shallow checkout.
Release readiness The exhaustive release-candidate gate. Its secret scanner checks the exact frozen candidate tree, and its evidence binds the complete quality, security, compatibility, and artifact results to one exact commit.
Release tag Verifies the exact approved readiness evidence, then deterministically rebuilds the distributions and SBOM and compares distribution hashes. It does not rerun unchanged pytest, lint, type, dependency-audit, or Semgrep work.

Version 1 security dependency governance

The release-project proof, release-readiness, and release workflows use the same checked-in GraphQL query and schema-v1 policy to validate Version 1 security sequencing. The policy names the exact Project and repository, the required issue owner and release iteration, every required native GitHub blocked by relationship, the permitted iteration order, and #131 as the release-evidence consumer. Workflow-local variants of the query are rejected by contract tests.

The verifier fails closed with stable coded errors when Project pagination or a required field cannot be verified, an issue is missing, ownership or iteration is wrong, an edge is missing or contradicted by its reverse, the graph has a cycle, a prerequisite is scheduled after its dependent, or a dependent is closed while a prerequisite remains open. Its schema-v1 JSON report contains only issue numbers, reviewed Project coordinates, dependency pairs, the canonical policy digest, and status. It excludes titles, bodies, comments, tokens, environment values, user paths, and response bodies.

Release evidence accepts the version-1-security-dependencies result only when the report matches the current policy digest and exact policy-derived issue, dependency, Project, repository, and #131 consumer sets. A stale, substituted, partial, or structurally unknown report cannot satisfy the gate. This control still relies on authorized GitHub maintainers plus GitHub’s Project, issue, dependency, API, and token behavior; API unavailability or insufficient access leaves the gate incomplete rather than passing.

Job timeout governance

Every job in the required CI, CodeQL, dependency-review, desktop, release-project proof, release-readiness, and release workflows has a reviewed literal timeout-minutes value. A closed workflow contract records the complete governed job set and rejects a missing job, an unreviewed job, an expression-based timeout, or a timeout placed after executable steps. Matrix limits apply independently to each matrix row.

The ceilings deliberately leave substantial margin above recent successful hosted runtimes while bounding a stalled runner:

Work class Reviewed ceiling Recent successful baseline
Coordination, classification, evidence, and proof 1-15 minutes Usually seconds
Python tests, audits, CodeQL, builds, and consumer installation 20-30 minutes Python jobs under 3 minutes; stock-pip platform installation about 1 minute or less
Native desktop package rows 45 minutes Under 9 minutes
Release installer validation and native release builds 60 and 90 minutes Intentionally wider because release signing, packaging, and platform services have greater variance

These observations are review baselines, not service-level guarantees. The wider limits accommodate ordinary GitHub-hosted Python, Node, cache, network, and platform variance. Queue delay before a runner starts and a broader GitHub Actions outage are outside a job timeout; missing or incomplete hosted evidence remains a failure to satisfy the gate rather than a pass.

The deterministic proof is an explicitly requested manual CI mode:

gh workflow run ci.yml --ref main -f timeout_proof=true

The expected workflow conclusion is failure. GitHub reports the timed-out exercise job itself as cancelled: it first uploads a schema-v1 armed record, then runs a fictional five-minute sleep under a one-minute job timeout. An always-run evidence job requires that exact cancelled result, uploads a sanitized confirmed record, emits a stable failure code, and deliberately fails so the proof cannot be mistaken for a successful required check. A failure, success, skipped, or other result fails verification without producing confirmed evidence. The records contain only fixed fixture identifiers, durations, schema/status fields, and the job result; they contain no secrets, environment values, usernames, hostnames, private or temporary paths, response bodies, genealogy data, or application payloads.

This governance changes repository workflow availability and evidence only. It does not change the application API, CLI registry, DTOs, provider behavior, GEDCOM representation, storage, FastAPI, or Electron boundaries, so ARCHITECTURE.md requires no change. The security benefit is bounded runner and quota retention plus timely fail-closed evidence; it does not mitigate a GitHub queue or platform outage.

Every workflow job that uses uv calls the repository-local setup-verified-uv composite action. The action performs the same policy preflight with the ephemeral job-scoped GitHub token, passes the policy-selected checksum to the exact pinned setup-uv commit with Astral mirror downloads disabled, re-hashes the installed binary before execution, and retains the schema-v1 receipt. Each calling job grants the verifier contents: read and attestations: read; jobs retain only any additional job-specific scope already required by their release contract. The token is not included in receipts or action outputs. Only the bounded gh attestation verify subprocess receives the token; version probes and all uv subprocesses receive an environment with GitHub token variables removed. An attestation verifier that exceeds 60 seconds fails with a stable coded error before uv can execute. The repository Actions allowlist permits only astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 for that external action. The policy, local action, and workflow contracts independently require the same commit. The receipt upload includes the ignored .tools directory and fails if the expected file is absent. The cache key includes uv.lock, Python version, runner operating system, and runner architecture. Release-readiness and release evidence include the receipt’s policy digest and verified identity through the required bootstrap-verification gate.

The explicitly enumerated wheel and source-distribution consumer smoke jobs continue to use stock pip. Those jobs test what supported non-uv consumers receive; they do not build release artifacts or provide an exception to the verified bootstrap for repository commands.

The PR gate job aggregates repository-owned pull-request jobs behind one stable check name. A conditionally skipped workflow audit is accepted; every other aggregate dependency must succeed. GitHub’s Dependency Review and CodeQL checks remain independent required security controls.

TruffleHog retains its provider detectors and fails on verified or unknown results. Pull-request and protected-push runs scan the event’s commit range; scheduled and manually dispatched CI scans the current main candidate tree; release readiness scans the exact frozen candidate tree. This keeps candidate evidence scoped to the source that can actually ship. GitHub secret scanning and push protection remain the repository controls for immutable Git history and incoming pushes; the candidate-tree scans do not replace those controls.

Workflow-level path filters are intentionally not used for the required CI workflow. A filtered-out workflow may never create its required check. Path classification happens inside the workflow so the aggregate gate always reports a conclusion.

The desktop workflow applies the same pattern through the stable Desktop gate. Its source-security job and six native unpublished-package rows emit exact-head machine-readable evidence. They do not establish production binary signing. Project-produced 0.x release installers must be unsigned; full trusted binary signing is deferred until and required starting with v1.0.0. Release validation still requires the exact installer bytes to install and execute on the supported OS matrix. See the desktop verification guide.

Semgrep rule policy

The Semgrep gate scans the whole repository with one generated, local config. Every upstream input is pinned by exact bytes and semantic content, and rules with identical IDs or matching logic are deduplicated before Semgrep runs. A changed archive, missing reviewed rule, conflicting rule ID, or unreviewed redirect fails closed.

The reviewed third-party additions deliberately cover only current repository surfaces:

  • ten generic command-line and transport-hardening rules from Trail of Bits;
  • twelve Python and JavaScript/TypeScript dynamic-execution and high-signal obfuscation rules used by Apiiro PRevent, sourced from its separate malicious-code ruleset;
  • two GitHub Actions workflow-command rules from elttam.

The 0xdea rules are not loaded because their maintained rules target C and C++, which this repository does not contain. Trail of Bits rules for absent frameworks and container tooling, overlapping secret rules, Apiiro rules that produced false positives or parser failures, and elttam’s manual-audit and absent-framework rules are also excluded. Revisit this allowlist when the repository adds a language or framework; do not add a whole upstream pack without a clean scan and an overlap review.

Ruleset migration for the pull-request matrix

The pull-request install-matrix reduction must be introduced in two phases. The feature branch records those phases as separate commits.

Phase A: establish the aggregate gate

Deploy the first commit while retaining all nine existing install-smoke matrix contexts. Confirm a pull request reports a successful PR gate alongside every check currently required by the main ruleset.

After that successful hosted observation, update the ruleset to require the stable PR gate rather than individual repository-owned CI job and matrix names. Keep the independent hosted security checks (currently Dependency Review and CodeQL’s Analyze (Python)), signed commits, linear history, review, and conversation-resolution protections.

Phase B: reduce the pull-request matrix

Only after the ruleset requires PR gate, deploy the second commit. Pull requests will then run the Ubuntu/Python 3.12 wheel-install cell plus the separate source-distribution smoke test. Pushes to main, schedules, and manual dispatches continue to run all nine wheel-install combinations.

Do not merge both commits at once while the ruleset still requires individual matrix contexts. The eight intentionally absent pull-request contexts would not be produced, so the old ruleset could leave the pull request waiting indefinitely.