Contents

Sites, environments and deployment flows

Read the site, environment and flow records that define deployment targets and pipeline execution.

Table: sites

Deployment targets for projects. A site represents a deployable unit (e.g., a web app, API service).

id INTEGER PRIMARY KEY -- internal surrogate key; never an operator input
project_id INTEGER NOT NULL REFERENCES projects(id)
name TEXT NOT NULL -- sole human identifier, unique within the project
description TEXT -- human-readable description
created_at TEXT NOT NULL -- app-supplied ISO-8601 UTC; see "Timestamp discipline" in Related
settings TEXT DEFAULT '{}' -- structured site configuration
UNIQUE(project_id, name)

Seed data: none — a fresh universe seeds no sites; rows are registered through projects.site.create (yoke projects site create, idempotent already_present) when the first work that deploys needs one, with settings maintained through the projects settings surfaces.

Table: environments

Deployment environments for sites (e.g., prod, stage). local is a machine-config client concept, not a deploy-target environments row.

id INTEGER PRIMARY KEY -- internal surrogate key; never an operator input
site INTEGER NOT NULL
project_id INTEGER NOT NULL REFERENCES projects(id)
name TEXT NOT NULL -- sole human identifier, unique within the project
url TEXT -- public URL (e.g., 'http://100.115.178.33:3000')
deploy_method TEXT -- e.g., 'github-actions', 'rsync+docker'
deploy_command TEXT -- shell command to run for deployment
health_check_url TEXT -- URL to check after deployment
config_notes TEXT -- human-readable notes about the environment
last_deployed_at TEXT -- stamped on successful run completion and on release_pin.record
created_at TEXT NOT NULL -- app-supplied ISO-8601 UTC; see "Timestamp discipline" in Related
settings TEXT DEFAULT '{}' -- structured environment configuration
UNIQUE(project_id, name)
FOREIGN KEY(site, project_id) REFERENCES sites(id, project_id)

Seed data: a fresh universe seeds no sites or environments — onboarding registers rows via projects.site.create / projects.environment.create (idempotent already_present) only when the first work deploys, with structured settings maintained through the projects settings surfaces. The operator's own registry rows (sites, environments, capability settings) live in the operator's private ops repo and are applied by operator tooling.

Table: deployment_flows

Deployment flow definitions. Each flow defines an ordered sequence of stages that an item passes through after merge.

id TEXT PRIMARY KEY -- e.g., 'project-prod-release'
project_id INTEGER NOT NULL REFERENCES projects(id)
name TEXT NOT NULL -- display name (e.g., 'Prod Release')
description TEXT
stages TEXT NOT NULL -- → JSONB on Postgres; JSON array of stage objects [{name, step_runner, ...}]
on_failure TEXT DEFAULT 'halt' -- failure policy: 'halt' stops the pipeline
created_at TEXT NOT NULL -- app-supplied ISO-8601 UTC; see "Timestamp discipline" in Related
target_tier TEXT -- persistent | ephemeral | NULL (merge-only)
target_environment_id INTEGER -- internal REFERENCES environments(id); required exactly when target_tier='persistent'
done_description TEXT DEFAULT NULL -- per-flow "done means..." contract; human-readable definition of what "done" means for this flow
status TEXT NOT NULL DEFAULT 'active' -- 'active' or 'disabled'
definition_schema_version INTEGER NOT NULL DEFAULT 1 -- stage vocabulary only
takes_delivery_custody INTEGER NOT NULL DEFAULT 0 -- 1 takes delivery of carried items
supersedes_flow_id TEXT REFERENCES deployment_flows(id)
UNIQUE(project_id, name)

Every stage object requires name (string), step_runner (string, closed set), and stage_kind (execution or qa). Valid step runner types: auto, health-check, warm-up, environment-activate, core-container-deploy, ephemeral-deploy, ephemeral-teardown, ephemeral-verify, human-approval, github-actions-workflow, qa. A database is brought up to its code by the boot converge that starts the container, so applying a migration is not a deployment stage and there is no stage kind vocabulary.

Every stage declares its runner fields at the top level, beside name and step_runner; a stage carrying a nested config object is refused on write, because the pipeline builds a stage's runner config from the stage itself and nested fields would never reach the runner. Normalization for execution keeps the stage the definition declared, so target, stage_kind, and scope are readable by the receipt layer and the preview producer. Python owner: yoke_core.domain.flow_validation.

Definition schema v2 declares execution and QA stages. QA stages declare scope, use step_runner: "qa", target a persistent environment or an earlier preview, may select reusable QA cases, and declare verdict authority separately from informational notification. Validate against the serving runtime before activation. Schema support and supported QA target kinds are separate admission checks; a schema floor does not promise an unsupported target. Delivery custody is the independent takes_delivery_custody declaration. Create accepts --takes-delivery-custody true|false; omitting it stores true. Editing stages does not change custody.

human-approval: the driver asks the serving control plane to evaluate the exact run/stage through deployment_runs.stage_approval.evaluate (yoke deployment-runs stage-approval evaluate RUN-ID --stage STAGE). Evaluation raises the declared decision request and reports its wait; it never approves. Candidate code must not derive policy from a deployed database's different schema. deployment_runs.approve records the authorized answer. Approval wakes the run's live driver, or the project's steering seat when none is attached, with same-run re-entry commands; the runner alone advances state. Rejection closes the run. Owners: deployment_approval_requests, handlers.deployment_stage_approval, deployment_stage_approval_dispatch, deployment_stage_decision_effect, reached through decision_request_subject_effect in yoke_core.domain.

github-actions-workflow step runner: Triggers a GitHub Actions workflow and polls for completion. Stage fields: workflow (workflow filename, e.g., deploy.yml), watch_for (state to wait for, e.g., "completed"), on_failure ("halt"). Used by external projects where GitHub Actions owns the pipeline. Python owners: yoke_core.domain.github_actions + yoke_core.domain.deploy_pipeline.

The hosted platform-release-bridge.yml dispatch also carries control_plane_api_url, sealed from the connection owning the run. A self-replacing driver uses its paired HTTPS connection's api_url; the deployment target does not select release authority. The bridge and its scoped consumer proof bootstrap use this endpoint. Missing or unsafe endpoints refuse with release_control_endpoint_unavailable before dispatch.

A workflow stage normally dispatches its ref branch (default main), whose workflow source may differ from {head_sha} checkout inputs after the branch moves. For builds requiring workflow/ref/checkout to be one attested commit, set run_from_release_commit: true. The boolean applies only to workflow stages and excludes ref. github_actions.dispatch_tag.ensure creates or confirms the lightweight yoke-deploy/<run-id> tag at the release commit, then dispatches it. Tags are retained and create-only: another existing SHA refuses with dispatch_tag_conflict; an unavailable commit with dispatch_tag_commit_missing. Re-drive uses the same tag. Exclude yoke-deploy/ from tag-push workflow triggers. Owners: deploy_pipeline_release_commit_ref, handlers.github_actions_dispatch_tag.

input_bindings can bind workflow input names to registered project branches, e.g. {"consumer_sha": {"project": "other", "branch": "main"}}. Run start resolves each once into deployment_runs.bound_sources; unreachable branches refuse and conflicting branches for one project refuse. Stages substitute stored commits. Start enrollment admits the bound project's delivery-ready items as ordinary members; delivery is judged against that exact recorded commit. Each project record's outputs stores only commits actually produced by the run, e.g. {"commit_sha": "...", "reason": "release_pin_materialization"}; other unowned commits remain outside-Yoke changes. Hosted promotion_receipts retain verified attempt artifacts; served-identity QA uses the artifact's deployed SHA, including no-op pins, without re-resolving a branch. Owners: deployment_run_bound_sources, deployment_run_project_sources, deployment_run_release_output, deployment_run_release_output_record.

A bound workflow stage is not dispatched when its target already serves the run's commits. Before the CI gate, the driver asks deployment_runs.execution.delivery_reuse. It reads the newest succeeded run, in any flow, on an environment with this run's name, for this run's project or a project the stage binds. If that run's promotion receipt for every bound project names this run's release_lineage as product_sha and its bound commit as platform_sha, the stage completes: the receipts are copied into this run, the entries are sealed consumed_by the stage, and bound_sources.delivery_reuse[STAGE].run_id names the reused run. Later stages such as warm-up still run. Any difference dispatches, and the driver prints why: a changed commit, a newer deploy from another flow, or an earlier run that failed or has no receipt. --fresh always dispatches, and so does a serving build without the function. Owner: deployment_run_delivery_reuse.

warm-up: makes one heavy relayed call against the rolled environment. Required connection_env names its client connection; function defaults to read-only board.data.get; timeout_s defaults to 180. Pass requires an actual answer. DeploymentRunWarmedUp records function, connection and latency; transport/function failure fails the stage. Owner: yoke_core.domain.deploy_warm_up.

health-check step runner: An explicit stage url is checked verbatim (plain HTTP 2xx, no request-id contract assumed for arbitrary endpoints). When the stage omits url, the URL resolves from the flow's referenced environment settings as the declared hosts.api URL plus health_path and the check enforces the Yoke core x-request-id echo contract: the request carries a generated x-request-id header and fails unless the response echoes the exact same value back.

Read the current project workflow definition with yoke workflows definition get --project <slug> --json; inspect a flow with yoke deployment-flows get <flow-id> / stages.

Flows are ordinary control-plane rows, managed by command like every other database object. Define one with yoke deployment-flows create <flow-id> --project <slug> --name NAME --stages-file PATH ([--takes-delivery-custody true|false]), adding --target-tier persistent --environment <name> for a flow that deploys to a registered environment or --target-tier ephemeral for per-run preview substrate. Validate advanced configuration with yoke deployment-flows validate, replace any unused definition atomically with deployment-flows update, reorder its complete stage set with deployment-flows reorder, or publish the successor to a used definition with deployment-flows version. Change lifecycle state with yoke deployment-flows set-status <flow-id> active|disabled; disabling is how a route is retired — it prevents new assignments and runs while preserving the definition and every historical run. A definition referenced by a run is immutable except for its lifecycle status, so changing its configuration requires a new version linked by supersedes_flow_id. The project default lives in the deploy_defaults Project Structure family: read it with yoke project-structure deploy-defaults get --project <slug> and set it through yoke project-structure patch apply.

Schema initialization creates the registry but never seeds a project's delivery topology, and no file in a project repository defines it either. Every project—without exception—owns its flow IDs, stage names, workflow filenames, retirements, and default as control-plane rows. Runtime behavior comes from stored stages and capabilities, not from a recognized project slug or flow-ID prefix.

Flow ids are definitions, not executions. A merge-only flow has null target_tier: merge close-out runs the remaining declared lifecycle gates through done, including pass-through release stages, without a release run. A level change at a pass-through stage does not interrupt that close-out. This also works for a local checkout with no GitHub repository. Creating a release run for that flow refuses with merge_only_flow_no_release_run; finish merge close-out instead, or select a flow with a deployment target when the work must deploy.

Flows with a deployment target create concrete run-... ids through /yoke usher, and each run retains its definition relationship for durable history.

Deployment run authority

Deployment runs define lineage, composition and idempotent creation; stage receipts define executor evidence; members and QA define custody, settlement, target occupancy, run drivers and blocking QA. Read these before creating or executing a run or interpreting release completion.

Run lifecycle events (type='deployment_run', such as DeploymentRunStageStarted or DeploymentRunWarmedUp) are written once per run, carry run_id in their context, and name no member item. Find a run's members in deployment_run_items; the events are telemetry, never the record of what a run carried.

Table: deployment_preview_environments

Preview environment occupancy tracking for deployment runs.

id INTEGER PRIMARY KEY
project TEXT NOT NULL REFERENCES projects(id)
env_name TEXT NOT NULL -- e.g., 'stage', 'sandbox'
run_id TEXT REFERENCES deployment_runs(id)
status TEXT NOT NULL DEFAULT 'available' -- available | claimed | stale
url TEXT
created_at TEXT NOT NULL -- app-supplied ISO-8601 UTC; see timestamp discipline in the introduction
env_type TEXT NOT NULL DEFAULT 'adhoc' -- shared | adhoc
UNIQUE(project, env_name)

Table: ephemeral_environments

Tracks per-branch ephemeral environments for pre-merge E2E validation. GitHub Actions creates environments; Yoke tracks lifecycle through the registered yoke ephemeral-env get, create and update adapters on the selected connection.

id INTEGER PRIMARY KEY
project TEXT NOT NULL REFERENCES projects(id)
branch TEXT NOT NULL -- MUST use 'PREFIX-{id}' format (see Branch Naming Contract below)
item TEXT -- backlog item (e.g., 'PREFIX-N')
workflow_run_id TEXT -- GitHub Actions run ID that created this environment
github_ref TEXT -- git ref used for the environment
port_api INTEGER
port_web INTEGER
url TEXT
status TEXT NOT NULL DEFAULT 'pending' -- pending | starting | running | healthy | stopped | failed
started_at TEXT
stopped_at TEXT
health_check_url TEXT
deployed_sha TEXT -- commit SHA of last pushed code (enables push/poll short-circuit)
created_at TEXT
UNIQUE(project, branch)

Branch Naming Contract

The branch column MUST use the value PREFIX-{id} (matching the item's worktree branch name, e.g., PREFIX-N). This convention is required for conduct compatibility -- the conduct skill queries ephemeral environments by branch='PREFIX-{id}' at steps d2, E1, and E3 in dispatch-context.md. CI systems that write ephemeral environment records (e.g., external-webapp-ephemeral.yml) must use the same PREFIX-{id} branch value. If a future project uses a different branch naming scheme, both the CI workflow and the conduct skill query must be updated in lockstep.

Sites, environments and flows · Yoke