33 KiB
CI/CD Standardization
This document defines the stable split between CI artifact producers, artifact catalog data, legacy guardrails and CD consumers. Detailed Tekton rules remain in docs/reference/ci.md; registry runtime and consumers remain in docs/reference/artifact-registry.md; user-service delivery order remains in docs/reference/user-service-delivery.md.
Target Shape
The standard release shape is:
- CI builds or locks the artifact from a pushed commit or verified upstream digest.
- The artifact is published to the D601 artifact registry or a controlled mirror.
- Dev validation consumes that same artifact.
- Production CD is pull-only and verifies live commit or upstream digest.
- Runtime metadata can be traced back to a live Git commit or upstream image digest.
backend-core and D601 code-queue may be validated only in dev in this phase. This document must not be used to introduce production deploy validation for either service.
Artifact Catalog
Root CI.json is the CI producer catalog. It is not a deployment manifest.
Allowed catalog data:
- stable
serviceId; - artifact
kind:source-buildorupstream-image; - producer command:
ci publish-backend-coreorci publish-user-service; - source repository URL, optional repo root and repo-relative Dockerfile path;
- image repository naming and commit tag policy;
- upstream image digest, upstream source revision and D601 mirror intent for image-only services;
- the required success summary contract.
Forbidden catalog data:
- provider IDs;
- runtime namespace, Compose service, Kubernetes Service or health path;
- ports, environment variables, replicas or volumes;
- desired deployment commits or rollout targets.
Runtime topology belongs to config.json, deploy.json, existing Kubernetes manifests and the artifact-registry executor.
Producer Contract
bun scripts/cli.ts ci publish-user-service --service <id> --commit <full-sha> reads source.repo, source.dockerfile and image repository naming from CI.json. It rejects command-line repo overrides. Successful source-build producers must label the image with:
unidesk.ai/service-id;unidesk.ai/source-commit;unidesk.ai/source-repo;unidesk.ai/dockerfile.
The successful artifactSummary must contain serviceId, sourceCommit, sourceRepo, dockerfile, imageRef, tag, digest and digestRef.
blocked catalog entries must return a structured blocked result. They must not silently skip, build from a dirty worktree, fall back to target-side maintenance deployment, or mutate production.
Current Coverage
Supported source-build artifact producers:
backend-corethroughci publish-backend-core;frontend,baidu-netdisk,decision-center,project-manager,oa-event-flow,todo-note,code-queue-mgr,findjob,pipeline,met-nonlinear,k3sctl-adapter,mdtodo,claudeqqthroughci publish-user-service;code-queuethroughci publish-user-servicefor dev image validation only.
Cataloged but blocked:
filebrowserandfilebrowser-d601: upstream image-only services pinned todocker.io/filebrowser/filebrowser@sha256:289c5dd677c56662440f26eeb44266ed9746fe563d2e9100f546bff558534d70; they need a future upstream mirror producer before CI can publish them.
code-queue-mgr is a supported CI producer because the source-build input is known and the remote consumer commit already added a reviewed artifact consumer shape. Its production live apply remains supervisor-gated by deploy/artifact-registry and is not authorized by CI.json.
Upstream Image Consumers
filebrowser and filebrowser-d601 are upstream-image consumers, not source-built UniDesk services.
| Service | Upstream image | Source revision | Catalog home | CI Dockerfile build | Digest / mirror strategy | CD validation |
|---|---|---|---|---|---|---|
filebrowser |
docker.io/filebrowser/filebrowser:v2.63.3 |
ca5e249e3c0c94159c2136a0cd431a424eb18472 |
CI.json.artifacts[] with kind=upstream-image plus config.json.microservices[].repository.artifactSource |
forbidden | resolve tag to docker.io/filebrowser/filebrowser@sha256:<manifest-digest>, then optionally mirror to 127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:<manifest-digest> |
pull by digest or mirror digest, verify OCI labels, container image id/digest, and private proxy health |
filebrowser-d601 |
docker.io/filebrowser/filebrowser:v2.63.3 |
ca5e249e3c0c94159c2136a0cd431a424eb18472 |
CI.json.artifacts[] with kind=upstream-image plus config.json.microservices[].repository.artifactSource |
forbidden | same as filebrowser |
same as filebrowser |
The catalog records the resolved upstream digest for the current image. If a future tag refresh cannot resolve the registry manifest digest, rollout must remain blocked until a reachable registry path resolves the manifest digest and records the mirror digest. A local Docker image id is supporting evidence only and not a registry digest pin.
D601 Direct Services
D601 direct / host-managed services keep runtime ownership outside native k3s. Their standard path is still the artifact split: CI builds a commit-pinned image on D601, CD on D601 only checks the registry manifest, pulls or retags the artifact, recreates the existing Compose service with --no-build --no-deps --force-recreate, and verifies image labels plus service health. The provider-gateway/SSH path is a controlled maintenance bridge for those D601-local actions, not a target-side build standard and not a new public ingress.
| Service | Producer | Consumer | Dev validation | Prod validation | Blocker |
|---|---|---|---|---|---|
findjob |
ci publish-user-service --service findjob builds Dockerfile from https://gitee.com/Lyon1998/findjob into unidesk/findjob:<commit> |
D601 direct Compose artifact consumer, service server, container findjob-server |
deploy apply --env dev --service findjob --dry-run plans pull-only CD and live apply is allowed when the artifact exists |
deploy apply --env prod --service findjob --dry-run plans the same path; prod live apply is allowed by policy |
health does not report deploy commit, so strict commit proof relies on image and container labels plus /api/health |
pipeline |
ci publish-user-service --service pipeline builds Dockerfile from https://github.com/pikasTech/pipeline into unidesk/pipeline:<commit> |
D601 direct Compose artifact consumer, service pipeline-control, container pipeline-v2-control |
deploy apply --env dev --service pipeline --dry-run plans pull-only CD and live apply is allowed when the artifact exists |
deploy apply --env prod --service pipeline --dry-run plans the same path; prod live apply is allowed by policy |
health does not report deploy commit, so strict commit proof relies on image and container labels plus /health |
met-nonlinear |
ci publish-user-service --service met-nonlinear builds docker/unidesk/Dockerfile.ml from https://github.com/pikasTech/met_nonlinear into unidesk/met-nonlinear:<commit> |
D601 direct Compose dry-run contract for service met-nonlinear-ts |
deploy apply --env dev --service met-nonlinear --dry-run returns runtime-verification-blocked until the runtime image contract is fixed |
deploy apply --env prod --service met-nonlinear --dry-run returns runtime-verification-blocked; live prod apply is unsupported |
catalog Dockerfile is the ML image while the long-running service is met-nonlinear-ts; publish a labeled artifact for the running TS service or separate the server Dockerfile contract |
k3sctl-adapter |
ci publish-user-service --service k3sctl-adapter builds src/components/microservices/k3sctl-adapter/Dockerfile from UniDesk into unidesk/k3sctl-adapter:<commit> |
D601 direct Compose plan/dry-run for service k3sctl-adapter, container k3sctl-adapter |
no normal dev target; it is the control bridge for dev/prod k3s visibility | deploy apply --env prod --service k3sctl-adapter --dry-run exposes the pull-only contract; live prod apply is supervisor-only |
must remain outside the k3s failure domain and be recoverable before any live replacement; worker automation must not replace it without explicit supervisor confirmation |
filebrowser-d601 |
no UniDesk source-build producer; CI.json marks it upstream-image/blocked |
future pull-only upstream digest or D601 mirror digest consumer only | not implemented | not implemented | must not be represented as a Dockerfile build; first implement upstream digest resolution and mirror governance for docker.io/filebrowser/filebrowser:v2.63.3 |
findjob, pipeline and met-nonlinear deliberately do not create NodePort, hostPort or new public business ports. Runtime traffic stays behind backend-core, provider-gateway and the configured private service proxy. k3sctl-adapter is a control bridge, not an ordinary business service; it must be versioned, dry-run verifiable and manually recoverable before live replacement.
Upstream Image Evidence
The catalog expression is intentionally minimal and parseable:
| Evidence command | Required result shape |
|---|---|
| `jq '.artifacts[] | select(.kind=="upstream-image") |
bun scripts/cli.ts config show with the File Browser artifactSource projection |
both services parse as kind=upstream-image, digestPinRequired=true, mirrorRepository=127.0.0.1:5000/upstream/filebrowser/filebrowser, ciDockerfileBuild=false, and pullOnlyCd=true |
docker manifest inspect --verbose docker.io/filebrowser/filebrowser:v2.63.3 |
must resolve the upstream manifest digest before rollout; if the registry request times out, rollout remains blocked |
bun scripts/cli.ts ci publish-user-service --service filebrowser --commit <full-sha> --dry-run |
returns ok=false with status=blocked, upstream digest/mirror metadata, and no Dockerfile source build |
The digest/mirror dry-run contract is:
- Resolve
docker.io/filebrowser/filebrowser:v2.63.3todocker.io/filebrowser/filebrowser@sha256:<manifest-digest>. - Mirror only that resolved digest to
127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:<manifest-digest>. - Record the digest ref used by CD; do not promote a mutable tag as release truth.
- Refuse pull-only CD while the digest is unknown or the mirror digest differs from the upstream digest.
Pull-only CD validation must be expressed as concrete checks:
| Check | Form |
|---|---|
| Pull source | docker pull docker.io/filebrowser/filebrowser@sha256:<manifest-digest> or docker pull 127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:<manifest-digest> |
| Runtime identity | docker inspect reports the expected image id/digest and OCI labels org.opencontainers.image.version=2.63.3 and org.opencontainers.image.revision=ca5e249e3c0c94159c2136a0cd431a424eb18472 |
| Service health | UniDesk private proxy health succeeds for filebrowser and filebrowser-d601; direct public exposure remains forbidden |
| Build absence | no docker build, docker compose up --build, CI Dockerfile producer, or source checkout is used for these services |
Frozen Special-Case Inventory
This table freezes the boundary between standard artifact services, upstream image consumers, bootstrap or maintenance paths, unclear manual-risk paths, and legacy paths that may be removed only after replacement evidence exists.
| Object | Classification | Current Path | Target Path | Cleanup Prerequisites | Risk If Misclassified | Next Stage / Remove Later |
|---|---|---|---|---|---|---|
filebrowser |
upstream-digest |
config.json registers D518 unidesk-direct with repository.dockerfile=docker.io/filebrowser/filebrowser:v2.63.3, artifactSource.kind=upstream-image, and provider-local docker run --restart unless-stopped. CI.json marks it blocked from source-build CI. |
Pull-only upstream image consumer: resolve docker.io/filebrowser/filebrowser:v2.63.3 to a manifest digest, optionally mirror that digest to D601 registry, run by digest, then verify image identity and private proxy health. |
Implement upstream digest resolver/mirror worker, record mirror digest, add container image-id/digest validation, preserve File Browser state paths and rollback runbook. | Treating it as source-build would build the wrong artifact; deleting the docker-run repair path before pull-only CD exists can break D518 file access. | Do not delete in the next stage; implement digest/mirror CD first. |
filebrowser-d601 |
upstream-digest |
Same upstream image and blocked CI catalog as filebrowser, but provider-local container is unidesk-filebrowser-d601 on D601. |
Same pull-only digest or mirror digest model as filebrowser, scoped to D601. |
Same digest/mirror worker and runtime verification, plus D601 path/volume audit. | Same source-build confusion; premature cleanup can remove D601 file access used by operations. | Do not delete in the next stage; implement with filebrowser. |
database |
upstream-digest |
Main-server Compose service uses postgres:16-alpine, named volume unidesk_pgdata_10gb, init SQL, and pinned config files. It is lifecycle-managed by server start/server stop, not by server rebuild. |
Upstream digest pin or mirror for PostgreSQL image; Compose remains volume-preserving and pull-only for the image. | Digest pin for postgres:16-alpine, database backup/restore drill, major-version upgrade policy, and no down -v or volume deletion. |
Data loss or unplanned PostgreSQL upgrade; a source-build model is meaningless for this service. | Keep; never remove as legacy. |
provider-gateway |
bootstrap-keep |
Main-server Compose server rebuild provider-gateway and provider provider.upgrade mode=schedule can repair the control bridge. Host SSH keys are mounted read-only for maintenance, while normal automation uses WebSocket/provider tasks. |
Standard source artifact publication for normal releases plus retained protected self-upgrade/repair path; Host SSH remains explicit maintenance only. | Prove provider-gateway artifact consumer or protected upgrade can recover a broken provider without SSH session lifetime coupling; keep Docker socket scope and loopback egress checks. | Removing the bridge repair path can strand providers and block CD, diagnostics, or registry relay. | Keep; not removable next stage. |
dev-frontend-proxy |
bootstrap-keep |
Main-server Compose service built from src/components/dev-frontend-proxy/Dockerfile, exposed on the dev frontend port, and rebuildable by server rebuild dev-frontend-proxy. |
Small bootstrap proxy kept as a main-server support service; optional future source artifact only after dev frontend proxy health and D601 dev UI rollout are covered. | Decide whether the proxy should become a standard artifact, add CI image and pull-only consumer if yes, and preserve dev UI recovery. | Deleting it breaks the public dev UI path even when production is healthy. | Keep; not removable next stage. |
server rebuild backend-core |
bootstrap-keep |
Local main-server Compose build/recreate, serialized by Compose lock and validated after up. Docs forbid using it for Rust iteration or standard prod CD. | Recovery-only path; standard production path is D601 CI artifact plus deploy apply --env prod --service backend-core. |
Backend-core artifact CD rollback proven under outage conditions; recovery runbook can repair a broken core without local Rust build. | Removing too early can block recovery when core or registry relay is degraded. | Keep; not removable next stage. |
server rebuild frontend |
bootstrap-keep |
Local main-server Compose build/recreate for production frontend; maintenance-only and not release evidence. | Standard frontend release is CI unidesk/frontend:<commit> plus dev/prod artifact consumer and health/image-label verification. |
Equivalent pull-only repair command, registry rollback, and frontend health verification runbook. | Local dirty bundle can become accidental production truth; deleting too early removes emergency UI repair. | Keep for now; degrade only after pull-only repair is proven. |
server rebuild baidu-netdisk |
bootstrap-keep |
Local main-server Compose build/recreate for Baidu Netdisk; maintenance-only. | D601 CI source-build artifact plus dev/prod pull-only consumer. | Pull-only repair and token/staging persistence validation. | Dirty rebuild can hide token/staging regressions; premature deletion can block storage service repair. | Keep for now; degrade after pull-only repair is proven. |
server rebuild todo-note, code-queue-mgr, project-manager, oa-event-flow |
bootstrap-keep |
Local main-server Compose rebuild for internal or direct main-server services; some have artifact consumer validation, while live prod apply may remain gated. | Convert each to CI artifact plus pull-only consumer and live health commit verification where supported; keep recovery entry until equivalent repair exists. | Per-service artifact consumer, runtime deploy metadata, rollback, and persistence validation. | Dirty local rebuilds can bypass deploy.json; deleting before rollback can break main-server service recovery. |
Keep; reassess service by service. |
server rebuild provider-gateway |
bootstrap-keep |
Local Compose rebuild for the main-server provider gateway. | Keep as bridge repair; normal upgrades should use protected provider upgrade or artifact path when available. | Same provider-gateway recovery proof as above. | May strand host-level observability and maintenance dispatch. | Keep. |
server rebuild filebrowser*, database, code-queue, k3sctl-adapter, unknown services |
allowed-remove-later for silent fallback only |
Historically the CLI threw a generic usage error; no valid main-server Compose rebuild exists for these objects. | Structured unsupported-server-rebuild result; no job creation, no source build, no Compose mutation. |
Operators must use documented upstream digest, k3s artifact, bridge repair, or manual-risk path instead. | Generic errors can lead operators to invent hand-built fallback commands. | Silent/generic fallback is removed; do not delete the real upstream/bootstrap runtime paths. |
deploy apply --env prod unsupported services |
needs-manual |
Environment-ref prod apply reads origin/master:deploy.json; services without reviewed prod artifact consumers return structured unsupported. |
Add service-specific CI producer, D601 registry artifact, prod consumer, and live commit/digest verification before enabling. | Silent target-side builds would bypass registry, dirty-worktree, and live verification guardrails. | Not removable as a class; enable per service only after review. | |
Local-manifest deploy apply --file ... for reviewed prod artifact consumers |
allowed-remove-later |
CLI blocks reviewed prod artifact consumers before source materialization and points to deploy apply --env prod --service <id>. |
Environment-ref deploy and artifact consumer only for standard production. | Confirm no recovery runbook requires local-manifest prod apply; provide rollback via registry. | Dirty local deploy.json or target-side source build can become production truth. |
Allow removing or further degrading after recovery runbooks move to env-ref artifacts. |
D601 target-side build path for dev backend-core |
bootstrap-keep |
deploy apply --env dev --service backend-core builds on D601 and rolls out only unidesk-dev. |
Keep as controlled dev/Rust boundary until backend-core dev artifact consumer replaces it. | D601 CI artifact publication, dev consumer, Rust check/build parity, and rollback evidence. | Removing it can block Rust validation on the resource-appropriate host. | Keep; not removable next stage. |
| D601 maintenance-channel direct deploy for non-bridge services | allowed-remove-later |
Direct provider host.ssh build/apply is blocked for normal D601 services; codex deploy is disabled. |
Only artifact consumers or reviewed native bootstrap may mutate D601 services. | Confirm no supported runbook still calls the disabled path; keep structured unsupported output. | A second deploy control plane can bypass registry artifacts and k3s verification. | Remove stale callers/docs after no references remain. |
artifact-registry deploy-backend-core |
allowed-remove-later |
Compatibility name returns a structured deprecated result; replacement is deploy apply --env prod --service backend-core. |
Use only deploy reconciler entry for backend-core prod CD. | All callers and docs stop using the compatibility action. | Bypasses common deploy guardrails if re-enabled casually. | Remove compatibility after callers are migrated. |
k3sctl-adapter |
bootstrap-keep |
D601 direct Compose control bridge outside k3s; artifact consumer is plan/dry-run only and live prod apply needs supervisor confirmation. | Keep outside the k3s fault domain; optionally publish artifact for controlled upgrade, but retain bridge repair. | Supervisor-approved live upgrade path, adapter health/control-plane validation, and rollback without losing Kubernetes access. | Removing or moving it into k3s can remove the bridge needed to repair k3s. | Keep; not removable next stage. |
| Maintenance SSH / manual Docker / manual k3s commands | needs-manual |
Available through provider-gateway Host SSH, WSL SSH, manual Docker, and native k3s commands for repair and investigation. | Explicit maintenance-only runbooks with bounded commands, no silent source builds, no dirty worktree release truth, and no default CD fallback. | For each manual entry, document owner, trigger, rollback, volume/scope safety, and replacement artifact path. | Manual state can become undocumented production truth or destroy bootstrap access if over-cleaned. | Do not delete globally; constrain and document per path. |
Dirty worktree, mutable latest tag, hand-mutated container state |
allowed-remove-later as release truth |
These may exist as local operator state or Docker cache, but they are not valid deployment evidence. | Standard releases use deploy.json commit, commit-tagged or digest-pinned images, image labels, and live commit/digest verification. |
Runtime label checks, digest pins, env-ref deploy, and runbooks that reject latest and dirty worktree inputs. |
Reproducibility and rollback are lost; a healthy old or hand-built container may be accepted accidentally. | Remove as accepted release evidence; keep only as diagnostic observations. |
Legacy Path Classification
| Path | Classification | Current guardrail | Cleanup condition |
|---|---|---|---|
deploy apply --env prod for services without a reviewed artifact consumer |
Must stay disabled/degraded | returns structured unsupported and refuses maintenance-channel fallback | enable only after each service has a documented artifact producer, dev consumer, prod consumer and live verification |
Local-manifest deploy apply for prod artifact consumers (backend-core, frontend, baidu-netdisk, decision-center) |
Must stay disabled/degraded as a standard prod path | blocked before source build; operator is pointed to deploy apply --env prod |
remove only after all production deploys use env-ref artifact consumers and no recovery runbook depends on local manifests |
artifact-registry deploy-backend-core |
Legacy compatibility entry | returns structured deprecated result; replacement is deploy apply --env prod --service backend-core |
remove after callers and docs use only deploy apply --env prod |
server rebuild frontend and server rebuild baidu-netdisk |
Maintenance / non-standard | docs classify as maintenance-only; standard release requires CI artifact plus dev/prod artifact consumer | keep until recovery runbooks have equivalent pull-only repair commands |
server rebuild backend-core |
Diagnostic/recovery only; not Rust iteration or prod CD | docs forbid Rust iteration and prod backend-core artifact CD through this command | keep for bootstrap/recovery until backend-core artifact CD and rollback are proven under outage conditions |
server rebuild dev-frontend-proxy, code-queue-mgr, project-manager, oa-event-flow, todo-note, provider-gateway on main-server Compose |
Bootstrap / recovery / diagnostic | still serialized through Compose lock and post-up validation | reassess service by service after artifact consumers exist |
provider.upgrade mode=schedule for provider-gateway |
Must be retained | protected upgrade path with validation; Host SSH self-rebuild remains forbidden | do not delete; it is the provider-gateway recovery path |
D601 direct docker build, kubectl apply, docker compose up --build used by deploy executor for allowed dev/backend bootstrap and native k3s setup |
Bootstrap / recovery / controlled dev | limited to allowed target-side executor, native k3s initialization, and dev backend-core; Code Queue prod is not enabled | convert only after artifact consumers or native bootstrap replacements exist |
D601 direct Code Queue / old codex deploy |
Must stay disabled/degraded | compatibility command throws; docs classify direct deployment as forbidden | wait for controlled Code Queue dev/prod CD worker |
File Browser docker run image-only path |
Needs later worker | now cataloged as upstream-image consumer; no CI Dockerfile build allowed | implement digest-resolved pull-only/mirror CD before treating it as standard deployment |
Guardrails Added
- Upstream-image services are represented in
CI.json.artifacts[]withkind=upstream-imageand inconfig.json.microservices[].repository.artifactSource; they are explicitly outside source-build producers. ci publish-user-servicereturns a structured blocked result for registeredupstream-imageservices instead of trying to interpretrepository.dockerfileas a source Dockerfile.- Local-manifest production deploy for reviewed artifact consumers is blocked before source materialization/build, so prod cannot silently fall back to target-side source build or a dirty worktree.
artifact-registry deploy-backend-coreis demoted to a structured deprecated result; backend-core production CD must enter throughdeploy apply --env prod.server rebuildreturnsunsupported-server-rebuildfor upstream image consumers, database, D601 execution/control-plane objects and unknown services; it does not create a job or mutate Compose for those objects.
Guardrail Evidence Matrix
| Guardrail name / result key | Command evidence | Legacy path covered | Deletion status |
|---|---|---|---|
upstream-image CI publish rejection |
bun scripts/cli.ts ci publish-user-service --service filebrowser --commit <full-sha> --dry-run returns ok=false, status=blocked, and upstream metadata |
File Browser accidentally entering Dockerfile CI | keep; deletion of docker-run repair waits for digest/mirror CD |
prod-artifact-consumer-local-manifest-blocked |
bun scripts/cli.ts deploy apply --file deploy.json --service frontend --dry-run returns ok=false, this error key, and points to deploy apply --env prod --service <service-id> --commit <full-sha> |
prod source-build fallback for reviewed artifact consumers | keep; local manifest mode may still be needed for non-prod/recovery until runbooks are replaced |
artifact-registry deploy-backend-core deprecated result |
bun scripts/cli.ts artifact-registry deploy-backend-core --commit <full-sha> returns ok=false, deprecated=true, and replacement deploy apply --env prod --service backend-core --commit <full-sha> |
backend-core prod CD bypassing deploy reconciler guardrails | keep name only as compatibility until all callers stop using it |
| prod unsupported result for services without artifact consumers | deploy apply --env prod --service <unsupported-service> --dry-run must return unsupported instead of falling back to source build |
target-side source build/maintenance-channel prod deploy | keep disabled until service-specific artifact consumers exist |
unsupported-server-rebuild |
bun scripts/cli.ts server rebuild filebrowser and bun scripts/cli.ts server rebuild database return ok=false, error=unsupported-server-rebuild, classification, replacement path and allowed services |
accidental source-build/Compose fallback for upstream images, database, k3s/control bridges and unknown services | keep; it removes only silent/generic fallback, not bootstrap or maintenance runtime paths |
| backend-core/code-queue prod boundary | docs and deploy support matrix allow backend-core and D601 Code Queue only in dev validation for this phase | accidental prod validation entrypoints for backend-core or Code Queue | do not add executable prod test/deploy validation in this precheck |
The guarded-but-not-deletable paths are: server rebuild backend-core, server rebuild frontend, server rebuild baidu-netdisk, provider-gateway protected upgrade, native k3s bootstrap, k3sctl-adapter bridge repair, File Browser provider-local docker-run repair, and D601 dev/backend target-side rollout. They remain because they are bootstrap, recovery, diagnostic, or controlled dev paths; deleting them requires replacement runbooks or reviewed artifact consumers.
Safety Boundary
CI may build images, push to the D601 loopback registry and report immutable digests. CI must not run production CD, call deploy apply for production, mutate production namespaces, recreate production Compose services or update deploy.json.
backend-core and D601 code-queue remain restricted to dev image validation in this phase. Any future production rollout for them must be implemented as an explicit CD consumer change, not as a CI producer side effect.
Code Queue Dry-Run Delivery Boundary
Code Queue follows the standard artifact split only up to a dev-only consumer:
| Stage | Owner | Allowed output | Explicitly forbidden |
|---|---|---|---|
| CI producer | Tekton / CI runner outside Code Queue self-deploy | Build from pushed Git, publish 127.0.0.1:5000/unidesk/code-queue:<commit>, report service id, source repo, source commit, Dockerfile, tag, digest and digest ref |
deploy apply, kubectl apply, scheduler/read/write rollout, task interrupt or cancel |
| CD dry-run | deploy/artifact-registry CLI | Read origin/master:deploy.json, show unidesk-dev target objects, required registry image, no-runtime-build validation and forbidden actions |
Mutating runtime objects, using dirty worktrees, using mutable tags, falling back to codex deploy or D601 maintenance-channel source deploy |
| DEV live apply | Human operator after dry-run evidence | Pull/import the existing commit image, update only unidesk-dev Code Queue scheduler/read/write/provider-egress-proxy objects, verify health through the Kubernetes API service proxy |
Touching production unidesk namespace, production PostgreSQL, production scheduler/runner, running task state or Code Queue Manager prod sidecar |
| PROD | Not implemented | Structured unsupported / dry-run evidence only | Any production Code Queue artifact deploy, manifest mutation, rollout restart, scheduler/runner rebuild, task interrupt/cancel, or self-deploy by the running Code Queue task |
The operator review points are fixed. DEV requires an operator to compare the CI artifact summary with deploy plan --env dev --service code-queue or deploy apply --env dev --service code-queue --dry-run, then explicitly authorize a DEV apply outside the Code Queue task. PROD has no apply authorization point in this phase; even a manual request must first land a separate reviewed Code Queue production CD design. The current runner may produce plans, preflight output, docs and contract tests only.
Lightweight contract evidence for this boundary is:
bun scripts/code-queue-cicd-dry-run-contract-test.ts
The test checks that dev targets only unidesk-dev, prod exposes no runtime deploy target, production mutation is unsupported, and dry-run output forbids Code Queue self-deploy, scheduler/runner mutation, interrupt and cancel actions.
Validation Boundary
This precheck uses lightweight parsing and dry-run evidence only. It intentionally does not run full check, e2e, Playwright, or other broad browser/runtime test suites on the master server because those are outside the precheck scope and may exceed master-server resource limits. backend-core and D601 code-queue production validation are also out of scope; backend-core dev rollout can be attempted only through the existing D601 dev path, and a provider-offline result is an infrastructure blocker rather than permission to validate production.
The structured read-only preflight entrypoints are artifact-registry status|health and ci publish-user-service --dry-run. Remote runners may call them through the frontend passthrough path, and the result must classify missing backend-core, database, provider or registry channels as runnerDisposition=infra-blocked. The detailed probe list remains in missingChannels; the stable runner-facing domain list is missingControlChannels with only backend-core, database, provider and registry. Those cases are infrastructure blockers, not business failures and not a license to retry a real publish. A non-dry-run publish may be attempted only where controlledPublish points: D601 CI, namespace unidesk-ci, PipelineRun unidesk-user-service-artifact-publish.