saiph decisions
Every saiph decision, by area. Generated from the decision files at build time.
| ID | Decision | Status | Record |
|---|---|---|---|
| DL-602 | saiph-ci is a first-party Go service that runs each shard of targets as one Kubernetes Job with backoffLimit 0 and adopts no CI engine. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-603 | Saiph CI triggers from the store’s WatchRefs and review’s WatchEvents, each resumed at a cursor stored in the same transaction as the runs it creates. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-604 | A Saiph CI target is a build-graph task id; affected targets come from the store’s snapshot Diff, fed to moon on stdin in a skeleton of graph-input files, with all as the barrier. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-605 | Saiph CI policy is one .saiph/ci.yaml, read at the run’s base and never from the change under test. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-606 | Each (repo, changeset, target) has one immutable Saiph CI result; CI never retries a target and a Job never retries a pod; INFRA is red, alerts, and is stored on the run only. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-607 | Saiph postsubmit runs in TAP-style milestones; a red target opens a breakage that keeps it red at trunk until a later trunk changeset passes it; culprit finding is flake-aware Bayesian search that runs each (target, changeset) at most once, and it comments, never reverts. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-608 | A Saiph CI flake is two results that disagree on one input key; it opens a flake breakage, and the target is quarantined, not required, until the breakage closes. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-609 | Saiph CI Jobs get their source from a saiphd CSI inline ephemeral volume and have no git. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-610 | Saiph CD follows Rapid: a release is a protected release/* ref cut from a green milestone, a pick is a rebased trunk changeset, and stages run deploy targets behind human land approvals. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
| DL-611 | Saiph’s remote build cache is REAPI with SHA-256 on bb-storage, scoped per repo by instance prefix and written only by postsubmit; CI compute is capped by a namespace ResourceQuota, a per-repo shard limit and a budget alert that pages Matt. | Active (design, 2026-10-19) | ../../ci-cd.md#decisions |
client
Section titled “client”| ID | Decision | Status | Record |
|---|---|---|---|
| DL-590 | Saiph’s client follows Scion’s tiers: the saiph CLI, one saiphd per node and a regional saiph-cache answer the same id-read RPCs as saiph-server, tiering is strict, objects travel in their stored form, and every tier verifies each object’s id before it keeps it. | Active (design, 2026-10-19) | ../../client.md#decisions |
| DL-591 | Saiph fetch is want-only (trees recursively, then blobs, with no have list); checkouts are sparse and either virtual (FUSE through go-fuse, with an overlay) or materialized; the filesystem monitor is the FUSE journal or inotify in saiphd; EdenFS is a design reference only. | Active (design, 2026-10-19) | ../../client.md#decisions |
| DL-592 | A Saiph working copy sits on one changeset and is committed through server-side Commit; the saiph CLI shows change ids in jj’s reverse hex, keeps a per-workspace op log with undo, keeps bookmarks local and shows a smartlog; jj as backend or client is rejected. | Active (design, 2026-10-19) | ../../client.md#decisions |
| DL-593 | Saiph tokens are JWTs with iss, sub, aud, tenant, scope, exp and iat, at most one hour long; keys are discovered from the issuer URL; Kubernetes service-account tokens map to service:k8s/ |
Active (design, 2026-10-19) | ../../client.md#decisions |
| DL-594 | Agents hold no Saiph token: the vcs_* tools on a Saiph workspace go through the Runner’s relay to saiphd, which adds the session’s token; each repo’s backend is derived from repoCutoverState and never stored; the git backend is unchanged. | Active (design, 2026-10-19) | ../../client.md#decisions |
| DL-595 | A Compass session whose repos are all on Saiph has no GitHub egress and no GitHub credential. | Active (design, 2026-10-19) | ../../client.md#decisions |
| DL-596 | Saiph CI gets source through the CSI driver saiph.rigel.build as an inline ephemeral volume with the pod’s own token, and every Saiph client tier ships with a cap per layer, a paging budget alert and spend-cap rows. | Active (design, 2026-10-19) | ../../client.md#decisions |
git-bridge
Section titled “git-bridge”| ID | Decision | Status | Record |
|---|---|---|---|
| DL-621 | Saiph’s git export is a pure function and import round-trips: every imported git commit exports byte-identical, and the bridge proves it per batch and never force-pushes. | Active (design, 2026-10-19) | ../../git-bridge.md#decisions |
| DL-622 | After a repo’s switch to Saiph, GitHub main has one writer: a ruleset whose only bypass actor is the bridge’s own App enforces it, and the bridge checks it before switching. | Active (design, 2026-10-19) | ../../git-bridge.md#decisions |
| DL-623 | Saiph git import is trunk-only, first-parent diffed and refusing: it goes through store intake with CommitBatch, and unrepresentable history keeps the repo on git. | Active (design, 2026-10-19) | ../../git-bridge.md#decisions |
| DL-624 | Saiph cutover is a per-repo state machine in Compass (GIT, IMPORTING, NATIVE_MIRRORED, NATIVE_ONLY); the backend is derived, and rollback from NATIVE_MIRRORED loses no landed history. | Active (design, 2026-10-19) | ../../git-bridge.md#decisions |
| DL-625 | After cutover, allowlisted same-repo GitHub PRs become external Saiph review changes and land by merge; other PRs are closed with a comment. | Active (design, 2026-10-19) | ../../git-bridge.md#decisions |
| DL-626 | The Saiph git bridge is its own binary, schema and GitHub App; it mints its own tokens and leaves RIG-5558 unchanged. | Active (design, 2026-10-19) | ../../git-bridge.md#decisions |
merge-queue
Section titled “merge-queue”| ID | Decision | Status | Record |
|---|---|---|---|
| DL-612 | saiph-queue is its own service with the schema saiph_queue and one planner per repo under a lease; it reads review and is never called by it. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-613 | Saiph merge-queue lanes are not configured: two entries share a lane when their affected targets or changed paths meet; all, graph_changed and a .saiphqueue change are barriers. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-614 | A Saiph merge-queue batch is tested on a speculative projection of its lane, with a Zuul window per lane and at most batch_max entries. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-615 | The Saiph merge queue never reruns a target; a failure is judged by CI’s flake quarantine, successor probes with flake rates as priors, and prefix bisection on changesets CI has not tested; INFRA holds a batch until Requeue. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-616 | A Saiph merge-queue batch lands by one UpdateRef with chain; a lost CAS rebases the batch onto the new trunk and retests only targets the trunk delta touches, with no retest when the graph is unchanged and they do not meet; at most 100 attempts. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-617 | Saiph merge-queue auto-rebase is Rebase with drop_empty; the queue fixes only trunk conflicts, with a deterministic classifier first and then one chat-completion call whose answer may only choose lines the two sides wrote, written by Rebase with resolutions and uploaded as a CONFLICT_FIX version that passes the same CI. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-618 | A Saiph merge-queue eject is one unresolved change-level comment with a typed reason and the conflict or failed targets attached, with the owner in attention. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-619 | Saiph merge-queue fixer spend has a per-call token cap and per-repo and per-tenant daily micro-USD caps, reserved before each call, plus a budget alert that pages Matt; the fixer stays off until they exist. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
| DL-620 | Saiph merge-queue policy is .saiphqueue at the trunk head, with a per-repo gate of pre (default) or post; operator caps can only be lowered. | Active (design, 2026-10-19) | ../../merge-queue.md#decisions |
overview
Section titled “overview”| ID | Decision | Status | Record |
|---|---|---|---|
| DL-580 | Two backends sit behind one agent vcs_* tool surface, chosen per repo: the RIG-5558 git backend ships unchanged for repos on GitHub, and Saiph serves Saiph repos through the saiph CLI, where agents have no GitHub egress and only trunk is mirrored to GitHub as derived git commits; the tools, the Runner backend pick and the web UI hooks stay in Compass, and Saiph stays in its own repo. | Active (design, 2026-10-18) | ../../README.md#decisions |
review
Section titled “review”| ID | Decision | Status | Record |
|---|---|---|---|
| DL-597 | A Saiph review change is named by the store’s 16-byte change_id; a version is an immutable changeset plus its base, numbered densely from 1; review owns versions and the store keeps none. | Active (design, 2026-10-19) | ../../review.md#decisions |
| DL-598 | The Saiph stack graph is a DAG of open-change dependencies taken from changeset parents and kept apart from version history; each change is reviewed and approved alone and lands with or after what it sits on. | Active (design, 2026-10-19) | ../../review.md#decisions |
| DL-599 | Saiph approval is four gates (LGTM, owner coverage from the owners list in .saiphacl, complete required checks, and .saiphreview rules); approvals carry only across message-only versions, trivial rebases and conflict fixes the policy allows. | Active (design, 2026-10-19) | ../../review.md#decisions |
| DL-600 | Saiph review is its own service, saiph-review, serving saiph.v1.ReviewService from schema saiph_review; it learns of landings from the trunk ref log and never calls the queue, which pulls land units. | Active (design, 2026-10-19) | ../../review.md#decisions |
| DL-601 | Compass keeps the Saiph review UI and agent tools, proxies ReviewService for the browser with a token it mints for the user, and tells an agent it is its turn with a home-channel message. | Active (design, 2026-10-19) | ../../review.md#decisions |
| ID | Decision | Status | Record |
|---|---|---|---|
| DL-578 | Saiph’s store follows Scion: changesets hold ordered parents, a 16-byte change id and the files changed against the first parent, are created only by the server, and are encoded as deterministic CBOR with BLAKE2b-256 ids keyed per type; file contents live zstd-compressed in object storage; changesets, derived data, refs, the ref log and pins live in a metadata database (YugabyteDB YSQL for many servers, Postgres 16 or later on one node, one shared schema saiph); a ref moves only by a single-row compare-and-swap. | Active (design, 2026-10-18) | ../../store.md#decisions |
| DL-579 | Saiph’s server creates commits, rebases and merges; a conflict is a typed result (paths, jj-ordered terms, hunks) and is never stored; UpdateRef advances a ref by an ordered chain of changesets in one compare-and-swap; derived data (trees, commit graph, ACL manifest eager; path index and git commits on trunk; blame lazy) is a pure function of a changeset and its parents, written back to the database. | Active (design, 2026-10-18) | ../../store.md#decisions |
| DL-581 | Saiph is its own repo and product, with its own Connect package saiph.v1, its own schema and tenant GUC, and per-tenant object keys; it reads the caller from an auth.Principal built from a verified issuer token (Compass is one issuer); per-directory ACLs are in-tree .saiphacl files, and a denied read answers NOT_FOUND. | Active (design, 2026-10-18) | ../../store.md#decisions |