60 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.
deploy.json is the single release-intent source for deployment-time parameters that must stay aligned across CI/CD, dev and prod consumers, and runtime health checks. Other files and manifests may mirror those values for wiring or compatibility, but they must be derived from deploy.json and not become a second source of truth. The long-term standardization work is tracked in GitHub issue #60.
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.
Phase-One Deploy.json Consolidation Contract
Issue #60 moves CI/CD image deployment toward one release-intent source. The implemented deploy.json schema still carries only id, repo and commitId; this section defines the phase-one migration contract before changing runtime behavior.
Current duplicated configuration surfaces are:
- Service source and version:
deploy.json.environments.*.services[],config.json.microservices[].repository,CI.json.artifacts[].source, runtimeUNIDESK_DEPLOY_*metadata and service health payloads. - Image repository, tag and digest:
CI.json.artifacts[].image, artifact-registry consumer specs, Compose/Kubernetes image fields, registry digest probes and docs tables. - Runtime target metadata:
config.jsonprovider/deployment entries, Compose service/container names, k3s namespaces, Kubernetes Deployment/Service names, artifact-registry target specs and runtime manifests. - Deployment parameters: runtime env prefixes, required secret keys, health deploy metadata requirements, resource requests/limits, rollout strategy and dev/prod support gates.
- Consumer scope:
deploy.jsondev/prod service lists, deploy/artifact-registry allowlists, dev/prod Kubernetes manifests and service-specific documentation.
Phase one extends the desired-state model in this order:
- Keep existing
deploy.jsonid,repoandcommitIdas the deployed source/version authority. All environment-ref deploy commands continue readingorigin/master:deploy.json. - Add optional deploy-owned artifact identity fields after the static drift guard is green:
artifact.kind,artifact.repository,artifact.tag, optionalartifact.digestRef, andartifact.upstreamDigestReffor upstream-image services. For source-build services,artifact.tagmust equal the selected commit unless a future schema explicitly records a digest-only release. - Add consumer intent fields:
consumer.kind,consumer.dev.enabled,consumer.prod.enabled,consumer.supportLevel,consumer.targetRefandconsumer.noRuntimeSourceBuild. These describe whether the service ismain-server-compose,d601-k3s-managed,d601-direct-compose,upstream-digest,dry-run-onlyorunsupported; low-level object names still come from config/manifests until renderers exist. - Add deployment-time metadata that must be common across CI, dev CD and prod CD: deploy env prefix, health deploy-metadata requirement, required runtime secret key names, rollback policy, service port, health path, and resource profile identifiers. The planned first keys are
runtime.containerPort,runtime.healthPath,runtime.memory.request,runtime.memory.limit,health.deployMetadataRequiredandruntime.requiredSecretKeys. Secret values, credentials, volumes and host paths do not move intodeploy.json. - Teach CI producer dry-run, deploy plan and artifact-registry dry-run to render from
deploy.jsonfirst and compare mirroredCI.json/config.json/manifest values as derived copies. A mismatch is drift, not an alternate source of truth.
Phase two starts with low-risk D601 k3s user-service artifact consumers: dev/mdtodo and dev/decision-center. Their deploy.json entries now own the source-build artifact repository/tag policy, D601 k3s consumer target, stable image, manifest reference, service port, health path, memory request/limit and health deploy-metadata requirement. deploy plan --env dev --service <mdtodo|decision-center> and dry-run artifact consumer plans must read those fields from deploy.json; the k8s manifest and artifact-registry executor constants are derived mirrors checked by a structured deploy-json-drift preflight.
Fields that stay outside deploy.json during phase one:
CI.json: producer pipeline name, source root, Dockerfile path and success summary shape. Once artifact identity is indeploy.json,CI.json.artifacts[].image.repositorybecomes a compatibility mirror checked for drift.config.json: provider id, proxy route policy, Compose file/service/container names for services not yet migrated, development SSH/worktree details, and secret source locations. Fordev/mdtodoanddev/decision-center, port/health target values in dry-run are deploy-owned and config is no longer an alternate source.- Compose and Kubernetes manifests: volumes, PVCs, security context and rollout strategy remain concrete manifests. For
dev/mdtodoanddev/decision-center, container port, memory request/limit and deploy metadata env presence are deploy-owned values and manifest copies are drift-checked mirrors until a renderer owns the file. - Artifact-registry executor code: low-level pull/import/retag/recreate commands, registry probes and platform-specific verification scripts. For
dev/mdtodoanddev/decision-center, executor dry-run consumes the deploy-owned artifact/consumer/runtime contract; any hardcoded mismatch returnsdeploy-json-drift.
The drift contract is:
deploy.jsonwins for release intent.config.json.microservices[].repository.commitIdis compatibility data only and must not drive env-ref deploys.deploy.json.repoandCI.json.artifacts[].source.repomust match for every source-build service that appears indeploy.json.- Image tags used by CI/CD must be commit-pinned or digest-pinned. Mutable tags such as
latestare never valid release evidence. - Runtime topology may be mirrored in dry-run output, but deployment commands must treat it as derived target metadata and keep
noRuntimeSourceBuild=truefor reviewed artifact consumers. - Upstream-image services must stay
blockedin CI source-build publishing until an upstream digest or mirror digest consumer exists.
Lightweight evidence for this contract is bun scripts/issue-60-cicd-drift-contract-test.ts. The test parses local JSON/docs only; it does not publish images, deploy services, restart containers, run Playwright or run full e2e.
Focused second-stage evidence is bun scripts/issue-60-deploy-json-executor-preflight-contract-test.ts. It runs only dry-run/contract paths, verifies the dev/mdtodo and dev/decision-center executors read image, target, port, memory and deploy metadata requirements from deploy.json, and simulates drift to assert field-level expected/actual error output.
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.
Main-Server Compose User Services
Main-server Compose user services are normal source-build artifacts even though their runtime lives on the master server. CI builds them on D601, publishes a commit-pinned image to the D601 registry, and CD streams that artifact back to the master-server Compose project. The runtime target must not build source, use a dirty worktree, expose the service port publicly, or use server rebuild <service> as release truth.
| Service | Producer | Artifact | Consumer | Dev validation | Prod validation | Blocker |
|---|---|---|---|---|---|---|
baidu-netdisk |
ci publish-user-service --service baidu-netdisk from src/components/microservices/baidu-netdisk/Dockerfile |
127.0.0.1:5000/unidesk/baidu-netdisk:<commit> |
master-server Compose service baidu-netdisk, container baidu-netdisk-backend |
deploy plan/apply --env dev --service baidu-netdisk consumes the registry artifact and must expose the redacted runtimeSecrets source contract; dry-run is acceptable while the artifact, registry or secret source condition is absent |
deploy plan/apply --env prod --service baidu-netdisk recreates only baidu-netdisk with --no-build --no-deps --force-recreate, then verifies image labels, /health.deploy.commit, requested commit and auth health; current prod artifact/health evidence is aligned |
D601 registry artifact must exist; dev live apply remains blocked until canonical Compose env source has non-empty Baidu client id, client secret and token key plus logged-in auth health |
project-manager |
ci publish-user-service --service project-manager from src/components/microservices/project-manager/Dockerfile |
127.0.0.1:5000/unidesk/project-manager:<commit> |
master-server Compose service project-manager, container project-manager-backend |
deploy plan/apply --env dev --service project-manager consumes the registry artifact and verifies labels plus health; dry-run is acceptable while the artifact or registry is absent |
deploy plan/apply --env prod --service project-manager recreates only project-manager with --no-build --no-deps --force-recreate, then verifies image labels, /health.deploy.commit and requested commit |
D601 registry artifact must exist before live dev or prod apply |
Focused smoke for this class is intentionally narrow: health, running image labels/digest, live deploy.commit / deploy.requestedCommit, and one private proxy API check such as baidu-netdisk /api/transfers?limit=20 or project-manager /api/projects. Full e2e, Playwright, broad check, public-port probing and unrelated service restarts are outside this lane.
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.
D601 Lane C Closure Matrix
This matrix is the single review surface for the remaining D601 service lane. It separates execution-plane ownership, control-plane ownership, CI artifact production, CD consumption and acceptance scope. All commands listed here are non-mutating unless they explicitly say live apply; Code Queue live apply is limited to dev and must not be performed by a running Code Queue task.
| Service | Deployment / ownership | CI current state | CD current state | DEV acceptance | PROD acceptance | Blockers | Next task |
|---|---|---|---|---|---|---|---|
findjob |
D601 unidesk-direct execution service in Docker Compose; control path is backend-core -> provider-gateway private HTTP proxy -> D601 loopback /api/health. |
CI.json source-build supported through ci publish-user-service --service findjob; artifact is 127.0.0.1:5000/unidesk/findjob:<commit> from the external Gitee repo Dockerfile. |
Reviewed D601 direct Compose artifact consumer for service server / container findjob-server; CD is pull-only, retag, docker compose up -d --no-build --no-deps --force-recreate server, then label and health verification. |
Allowed after deploy apply --env dev --service findjob --dry-run plus a matching registry artifact; live dev apply is permitted by policy. |
Allowed after deploy apply --env prod --service findjob --dry-run plus artifact/operator review; no public port or target-side build. |
/api/health does not report deploy commit, so strict proof depends on image/container labels plus health. Registry health and artifact existence are required before live apply. |
Add or upstream a deploy metadata field in health; keep label-based proof as the minimum contract until that lands. |
pipeline |
D601 unidesk-direct execution service in Docker Compose; control path is backend-core -> provider-gateway private HTTP proxy -> D601 loopback /health and /api/ / /oa/. |
CI.json source-build supported through ci publish-user-service --service pipeline; artifact is 127.0.0.1:5000/unidesk/pipeline:<commit> from the external GitHub repo Dockerfile. |
Reviewed D601 direct Compose artifact consumer for service pipeline-control / container pipeline-v2-control; CD is pull-only, retag, docker compose up -d --no-build --no-deps --force-recreate pipeline-control, then label and health verification. |
Allowed after deploy apply --env dev --service pipeline --dry-run plus a matching registry artifact; live dev apply is permitted by policy. |
Allowed after deploy apply --env prod --service pipeline --dry-run plus artifact/operator review; no public port or target-side build. |
/health does not report deploy commit, so strict proof depends on image/container labels plus health. Registry health and artifact existence are required before live apply. |
Add health deploy metadata and keep OA Event Flow integration checks as a focused post-apply smoke. |
met-nonlinear |
D601 unidesk-direct GPU/business execution service in Docker Compose; control path is backend-core -> provider-gateway private HTTP proxy -> D601 loopback /health and /api/. |
CI.json source-build supported through ci publish-user-service --service met-nonlinear; cataloged artifact uses docker/unidesk/Dockerfile.ml from https://github.com/pikasTech/met_nonlinear. |
D601 direct Compose consumer is plan/dry-run only for service/container met-nonlinear-ts; dry-run exposes the no-build pull-only shape but returns runtime-verification-blocked. |
Dry-run/read-only only. deploy apply --env dev --service met-nonlinear --dry-run must remain blocked until the running service image contract matches the published artifact. |
Not authorized. Prod dry-run must remain runtime-verification-blocked; live prod apply is unsupported. |
Published artifact is the ML image contract while the long-running service is met-nonlinear-ts, so CD cannot prove the running container image label equals the requested commit. |
Split the TS server artifact from the ML image or publish a labeled artifact that exactly matches met-nonlinear-ts; then add live commit proof before enabling apply. |
k3sctl-adapter |
UniDesk-managed D601 direct Compose control bridge, outside the native k3s fault domain; it is the control path for k3s-managed services and must not be moved into k3s. | CI.json source-build supported through ci publish-user-service --service k3sctl-adapter; artifact is 127.0.0.1:5000/unidesk/k3sctl-adapter:<commit> from the UniDesk Dockerfile. |
Artifact consumer exposes plan/dry-run only for service/container k3sctl-adapter; live replacement is supervisor-only because replacing the bridge can remove the repair path for k3s. |
No normal dev target. DEV acceptance is read-only bridge health, service catalog/proxy checks and dry-run contract review only. | Dry-run/read-only only in this lane. Real prod replacement requires explicit supervisor confirmation, rollback proof and out-of-band recovery access. | Must remain recoverable while k3s may be broken; worker automation must not self-replace or k3s-manage the bridge. | Write a supervised bridge-upgrade runbook with rollback and out-of-band access checks; keep CLI dry-run as the standard preflight. |
code-queue |
Production execution plane is D601 native k3s (unidesk namespace) behind k3sctl-adapter; dev execution plane is unidesk-dev scheduler/read/write/provider-egress-proxy. Main-server code-queue-mgr is a separate control-plane sidecar. |
CI.json source-build supported through ci publish-user-service --service code-queue for dev image validation only; artifact is 127.0.0.1:5000/unidesk/code-queue:<commit>. |
Reviewed dev-only k3s artifact consumer updates only unidesk-dev Code Queue objects. deploy plan --env prod --service code-queue and artifact-registry deploy-service --env prod --service code-queue must stay unsupported. |
Allowed only as dry-run/source/contract evidence here; a later human-approved dev live apply may consume the artifact into unidesk-dev outside the running Code Queue task. |
Not implemented and not authorized. No production artifact deploy, manifest mutation, scheduler/runner restart, interrupt or cancel is allowed. | Production still has hostPath/source and active-run safety boundaries; self-deploy would couple the deployment actor to the target being replaced. | Keep contract tests and dev dry-run coverage; design a separate supervisor-approved production CD consumer before any prod mutation is considered. |
decision-center |
D601 native k3s user service; dev runs unidesk-dev/decision-center-dev, prod runs unidesk/decision-center, both behind backend-core -> provider-gateway -> k3sctl-adapter -> Kubernetes API service proxy. |
CI.json source-build supported through ci publish-user-service --service decision-center; artifact is 127.0.0.1:5000/unidesk/decision-center:<commit> from the UniDesk Dockerfile. |
Dev and prod are reviewed D601 k3s artifact consumers. Desired state, live health, and registry artifact must point at the same commit; drift where live is newer than deploy.json is corrected by repinning deploy.json, not by redeploying. |
Closed for artifact CD when deploy plan --env dev --service decision-center is no-build and health reports matching deploy.commit / deploy.requestedCommit. Focused product gates remain record CRUD, diary lifecycle and frontend Decision Center visibility. |
Closed for artifact CD when deploy plan --env prod --service decision-center is no-build and health reports matching deploy.commit / deploy.requestedCommit. Remaining acceptance is manual UI/product verification: health, records, diary editor, frontend page, no public business ports and live commit/artifact information. |
Product completeness and manual UI acceptance can remain open, but they are not deployment drift. Registry artifact digest and health commit are the release evidence. | Keep scripts/decision-center-desired-state-contract-test.ts in the lightweight script gate so future desired-state edits cannot reintroduce source-build or stale-commit drift. |
Minimum evidence for this lane is:
| Evidence | Command |
|---|---|
| Code Queue dev/prod boundary contract | bun scripts/code-queue-cicd-dry-run-contract-test.ts |
| Code Queue dev target shape | bun scripts/cli.ts deploy plan --env dev --service code-queue |
| Code Queue prod unsupported shape | bun scripts/cli.ts deploy plan --env prod --service code-queue |
D601 direct Compose dry-run for findjob / pipeline |
`bun scripts/cli.ts deploy apply --env dev --service <findjob |
| MET Nonlinear blocked dry-run | bun scripts/cli.ts deploy apply --env dev --service met-nonlinear --dry-run |
| k3s control bridge dry-run | bun scripts/cli.ts deploy apply --env prod --service k3sctl-adapter --dry-run |
| CI producer preflight | bun scripts/cli.ts ci publish-user-service --service <service> --commit <full-sha> --dry-run |
| Decision Center desired/live no-build drift guard | bun scripts/decision-center-desired-state-contract-test.ts |
| Issue #60 phase-one deploy.json drift guard | bun scripts/issue-60-cicd-drift-contract-test.ts |
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 |
allowed-remove-later |
Replaced by deploy apply --env dev --service backend-core artifact consumer, which imports 127.0.0.1:5000/unidesk/backend-core:<commit> into unidesk-dev/backend-core-dev without compiling. |
Keep only structured no-build artifact consumer; remove stale target-side callers/docs. | D601 CI artifact publication, dev consumer, Rust check/build parity, and rollback evidence. | Any fallback rebuild could bypass the CI artifact and hide Rust build failures. | Legacy path is no longer the standard dev route. |
| 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 dev | prod --service backend-core`. | Use only deploy reconciler entry for backend-core CD. | All callers and docs stop using the compatibility action. | Bypasses common deploy guardrails if re-enabled casually. |
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 dev | prod --service backend-core` |
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 native k3s setup or approved recovery |
Bootstrap / recovery | limited to native k3s initialization or documented recovery; dev backend-core CD is now artifact-only; 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, and File Browser provider-local docker-run repair. Dev backend-core target-side rollout is no longer a standard guarded path; its replacement is the D601 registry artifact consumer, and stale target-side callers/docs should be removed once no runbook references remain.
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 now has explicit dev/prod artifact consumers; CI still only produces the image and must not deploy it as a side effect. D601 code-queue remains restricted to dev image validation in this phase. Any future production rollout for Code Queue 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 tag/digest provenance, pull-only/no-runtime-build validation, selfBootstrapGuard, requiresSupervisorApproval 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. D601 code-queue production validation is out of scope. backend-core dev rollout must use the artifact consumer 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.
Next Stage Dispatch Matrix
This matrix describes the next promotion stage after dry-run coverage is in place. It favors correctness over throughput, keeps backend-core and D601 code-queue dev-only, and splits dev and prod only where the runtime policy already allows it.
| Service class | Target branch | CI current state | CD current state | DEV acceptance | PROD acceptance | Blockers | Suggested model |
|---|---|---|---|---|---|---|---|
backend-core |
master |
source-build supported through ci publish-backend-core |
dev + prod artifact consumer | dev artifact rollout to unidesk-dev/backend-core-dev with no CD compile |
prod artifact recreate with live commit proof | CI resource, registry artifact, and runtime health evidence | GPT-5.5 |
code-queue |
master |
source-build supported, dev-only | dev-only k3s consumer | dev artifact validation for unidesk-dev scheduler/read/write/provider-egress-proxy |
not implemented; must remain unsupported | production boundary, hostPath/source contract, scheduler/egress dependency health | GPT-5.5 |
frontend |
master |
source-build supported | dev + prod artifact consumer | commit-pinned dev rollout and /health.deploy.commit |
commit-pinned prod recreate and UI route verification | none beyond standard artifact/CD checks | GPT-5.5 |
baidu-netdisk |
master |
source-build supported | dev + prod artifact consumer | pull-only dev validation is diagnosable through runtimeSecrets.secretSource, requiredSecretsPresent, missingSecretKeys and recommendedAction; live dev apply waits for the secret source condition |
prod artifact and health are aligned; further prod action is only focused post-apply proxy/auth verification after operator approval | canonical Compose env secret source and /health.auth gate; no manual secret operation in worker tasks |
GPT-5.5 |
project-manager |
master |
source-build supported | dev + prod artifact consumer | dev artifact validation with /api/projects |
prod artifact validation with live commit proof | none beyond standard artifact/CD checks | MiniMax for dry-run/reporting, GPT-5.5 for release sign-off |
oa-event-flow |
master |
source-build supported | dev + prod artifact consumer | dev artifact validation with /api/diagnostics |
prod artifact validation with live commit proof | none beyond standard artifact/CD checks | MiniMax for dry-run/reporting, GPT-5.5 for release sign-off |
todo-note |
master |
external source-build supported | dev + prod Compose artifact consumer | consumer dry-run is ready; live dev remains blocked until the desired artifact exists | prod behavior is healthy, but runtime commit proof is absent until the no-build recreate lands image labels and health deploy metadata | registry artifact, runtime commit proof and health deploy metadata | DeepSeek for digesting external-source evidence, GPT-5.5 for final gate |
decision-center |
master |
source-build supported | dev + prod k3s consumer closed when desired/live/artifact commit match and dry-run stays no-build | dev artifact CD closed; remaining dev acceptance is focused record CRUD, diary lifecycle, doc-number uniqueness and frontend visibility | prod artifact CD closed; remaining prod acceptance is manual UI/product verification after health/live commit proof | doc-management completeness, PostgreSQL truth and UI acceptance; no deployment drift when desired/live/artifact are aligned | GPT-5.5 |
mdtodo |
master |
source-build supported | dev + prod k3s consumer | dev service is absent until the health-metadata-capable desired artifact is published and unidesk-dev/mdtodo-dev is created/verified |
prod is healthy at the previous annotated commit, but the desired artifact now points at the commit that added /health.deploy and /live.deploy; prod replacement remains blocked until dev proof exists |
registry artifact, dev service, runtime health metadata proof and prod runtime commit drift; no NodePort/hostPort/public backend exposure | MiniMax for prompt prep, GPT-5.5 for approval |
claudeqq |
master |
source-build supported | dev + prod k3s consumer | dev service is absent until the desired artifact is published and unidesk-dev/claudeqq-dev is created/verified |
prod health reports the desired commit and NapCat login, but artifact and event API proof remain open | registry artifact, dev service, event API surface; NapCat/backend port exposure must stay private | MiniMax for prompt prep, GPT-5.5 for approval |
findjob |
master |
source-build supported | dev + prod direct Compose consumer | pull-only dev validation on D601 with image labels and /api/health |
pull-only prod recreate with live commit proof | target-side compose health/labels only, no public business ports | DeepSeek for dry-run matrix drafting, GPT-5.5 for final gate |
pipeline |
master |
source-build supported | dev + prod direct Compose consumer | pull-only dev validation on D601 with image labels and /health |
pull-only prod recreate with live commit proof | runtime contract is commit-label + compose service identity | DeepSeek for dry-run matrix drafting, GPT-5.5 for final gate |
met-nonlinear |
master |
source-build supported | dev dry-run only | runtime-verification-blocked until long-running TS service image contract is fixed | not authorized | image contract mismatch between ML Dockerfile and TS runtime service | GPT-5.5 |
k3sctl-adapter |
master |
source-build supported | plan/dry-run only | no normal dev target; only control-bridge health and recovery evidence | prod live apply requires supervisor confirmation | bridge recovery, k3s fault-domain isolation, no worker self-replacement | GPT-5.5 |
filebrowser / filebrowser-d601 |
master |
upstream-image blocked | pull-only mirror target | digest resolution, mirror governance and private proxy health only | not in this phase | upstream digest/mirror worker not yet implemented | DeepSeek for evidence summarization, GPT-5.5 for blocker resolution |
Frontend lane closure evidence is intentionally lightweight and non-mutating:
bun scripts/frontend-artifact-lane-contract-test.ts
The contract fixes the current sample around one artifact lane: deploy.json dev/prod both request the same frontend commit, CI.json keeps the producer as ci publish-user-service, the D601 registry manifest digest is recorded, dev and prod health expose matching deploy.commit / deploy.requestedCommit, ci publish-user-service --dry-run is ready, and both CD dry-runs are artifact consumers with no source build.
User-service artifact gap reviews must report the same normalized fields for each service: desiredCommit, runtimeCommit, artifactExists, devStatus, prodStatus, blockedScopes and recommendedAction. The issue #9 gap contract is intentionally lightweight and non-mutating:
bun scripts/issue-9-mdtodo-health-metadata-contract-test.ts
bun scripts/issue-9-user-service-artifact-gap-contract-test.ts
The contract pins the current mdtodo, claudeqq and todo-note gap surface: deploy.json dev/prod desired commits, CI.json producer metadata, structured status fields and dev/prod artifact-consumer dry-runs. mdtodo also has a local health metadata contract that starts the service against a temporary Markdown workspace and verifies /health.deploy plus /live.deploy before publication. These tests do not publish artifacts, apply manifests, recreate services, restart services, run full check/e2e, or probe browser UI.
Planned parallelism for the next wave should be three lanes:
- Lane A:
frontend,baidu-netdisk,project-manager,oa-event-flow. - Lane B:
decision-center,mdtodo,claudeqq,todo-note. - Lane C:
findjob,pipeline,met-nonlinear,k3sctl-adapter,code-queue,backend-core.
Lane C must stay split into dev-only work for backend-core and code-queue, plus read-only or dry-run work for met-nonlinear and k3sctl-adapter. backend-core and code-queue must not be promoted into prod acceptance tasks in this phase.