# 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: 1. CI builds or locks the artifact from a pushed commit or verified upstream digest. 2. The artifact is published to the D601 artifact registry or a controlled mirror. 3. Dev validation consumes that same artifact. 4. Production CD is pull-only and verifies live commit or upstream digest. 5. 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-build` or `upstream-image`; - producer command: `ci publish-backend-core` or `ci 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 --commit ` 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-core` through `ci 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`, `claudeqq` through `ci publish-user-service`; - `code-queue` through `ci publish-user-service` for dev image validation only. Cataloged but blocked: - `filebrowser` and `filebrowser-d601`: upstream image-only services pinned to `docker.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:`, then optionally mirror to `127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:` | 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:` | 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:` | 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:` | 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:` | 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") | {serviceId, upstream, status}' CI.json` | both File Browser services show `upstream.imageRef=docker.io/filebrowser/filebrowser:v2.63.3`, a sha256 `upstream.digestRef`, `sourceRevision=ca5e249e3c0c94159c2136a0cd431a424eb18472`, mirror intent under `upstream/filebrowser/filebrowser`, and `status=blocked` | | `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 --dry-run` | returns `ok=false` with `status=blocked`, upstream digest/mirror metadata, and no Dockerfile source build | The digest/mirror dry-run contract is: 1. Resolve `docker.io/filebrowser/filebrowser:v2.63.3` to `docker.io/filebrowser/filebrowser@sha256:`. 2. Mirror only that resolved digest to `127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:`. 3. Record the digest ref used by CD; do not promote a mutable tag as release truth. 4. 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:` or `docker pull 127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:` | | 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:` 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 `. | 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[]` with `kind=upstream-image` and in `config.json.microservices[].repository.artifactSource`; they are explicitly outside source-build producers. - `ci publish-user-service` returns a structured blocked result for registered `upstream-image` services instead of trying to interpret `repository.dockerfile` as 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-core` is demoted to a structured deprecated result; backend-core production CD must enter through `deploy apply --env prod`. - `server rebuild` returns `unsupported-server-rebuild` for 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 --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 --commit ` | 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 ` returns `ok=false`, `deprecated=true`, and replacement `deploy apply --env prod --service backend-core --commit ` | 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 --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. ## 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.