---
name: unidesk-temporal
description: UniDesk Temporal 基础设施与工作流运维技能，覆盖 YAML-first 受控部署、native PostgreSQL、Kubernetes/逻辑 namespace、共享 gRPC、Web 管理入口、统一管理员密码、状态验证，以及 native SDK worker/workflow smoke。用户提到 Temporal、workflow、activity、worker、task queue、temporal.hwpod.com、Temporal namespace、Temporal Web 或 Temporal smoke 时使用。
---

# UniDesk Temporal

## 核心事实

- 配置真相位于 `config/platform-infra/temporal.yaml`。
- 数据库真相位于 `config/platform-db/postgres-nc01.yaml`：
  - 使用 NC01 host native PostgreSQL；
  - 禁止在 Kubernetes 内创建 PostgreSQL workload 或 PVC。
- Kubernetes namespace 固定为 `temporal`。
- 默认业务逻辑 namespace 固定为 `unidesk`，保留期由 owning YAML 声明。
- 集群内共享入口为 `temporal-frontend.temporal.svc.cluster.local:7233`。
- Web 管理入口为 `https://temporal.hwpod.com`。
- Web 用户名来自 owning YAML；密码只来自 `/root/.unidesk/.env/unified-admin-password.txt`，并通过 `unidesk-secret` 分发。

## 操作边界

- 基础设施变更必须加载 `$unidesk-ymalops`，只修改 owning YAML 和受控 CLI 渲染器。
- 部署、状态和验证只使用 `bun scripts/cli.ts platform-infra temporal ...`。
- 临时 smoke worker 可以在 NC01 host native 启动，并跳过 CI/CD；不得把临时 worker 固化为 Kubernetes workload。
- 正式应用 worker 的交付仍遵循应用自身的 YAML/Git/CI/CD 流程。
- 不得硬编码 Service ClusterIP；native client 每次从 Service 只读解析当前 ClusterIP。
- 不得读取、打印或从 Kubernetes Secret、Pod env、数据库和日志反解密码。
- 不得用原生 `kubectl apply/delete`、Helm 或 Compose 建立第二套 Temporal 部署入口。

## 部署顺序

1. 检查 native PostgreSQL 计划并受控收敛：

```bash
bun scripts/cli.ts platform-db postgres plan \
  --config config/platform-db/postgres-nc01.yaml
bun scripts/cli.ts platform-db postgres apply \
  --config config/platform-db/postgres-nc01.yaml \
  --confirm
```

2. 通过统一 Secret 配置同步 Web 管理密码：

```bash
bun scripts/cli.ts secrets plan \
  --config config/secrets-distribution.yaml \
  --scope temporal \
  --target temporal-nc01
bun scripts/cli.ts secrets sync \
  --config config/secrets-distribution.yaml \
  --scope temporal \
  --target temporal-nc01 \
  --confirm
```

3. 先 plan 和服务端 dry-run，再执行异步 apply：

```bash
bun scripts/cli.ts platform-infra temporal plan --target NC01
bun scripts/cli.ts platform-infra temporal apply --target NC01 --dry-run
bun scripts/cli.ts platform-infra temporal apply --target NC01 --confirm
```

4. 按 apply 返回的 job ID 查询终态，再验证运行面：

```bash
bun scripts/cli.ts job status <job-id> --tail-bytes 12000
bun scripts/cli.ts platform-infra temporal status --target NC01 --full
bun scripts/cli.ts platform-infra temporal validate --target NC01
```

5. Temporal ready 后只读核对共享公网入口；公网配置由 `master` merge 后的唯一 PaC authority 自动收敛：

```bash
bun scripts/cli.ts platform-infra public-edge status --target NC01
```

## 验收标准

- Temporal server 与 UI 的 `readyReplicas` 达到 YAML 声明值。
- frontend/UI endpoint 均存在。
- 数据库模式显示 `nc01-host-native-postgresql`，Secret 只披露 presence/fingerprint，且 `valuesPrinted=false`。
- 逻辑 namespace `unidesk` 存在。
- `https://temporal.hwpod.com/healthz` 返回 `200`。
- 未认证访问 Web 根路径返回 `401`。
- 管理页面使用 repo-owned typed `web-probe` 命令验收：

```bash
bun scripts/cli.ts web-probe product-smoke \
  --product temporal \
  --target NC01 \
  --profile admin-readonly
```

- 命令从 Temporal owning YAML 解析公网入口、runner、Basic Auth 合同、SecretRef、视口和证据上限：
  - 禁止传入 URL、用户名或密码覆盖；
  - 只输出 Secret presence/fingerprint、DOM/网络/控制台有界摘要和工件哈希；
  - 必须保持 `mutation=false`、`valuesPrinted=false`。
- `public-edge status` 可能被其他站点的探针拉低；必须同时核对 Temporal 单站点证据，不能把无关站点故障归因给 Temporal。

## Native Workflow Smoke

- 使用 `$unidesk-trans` 的远程临时实验合同：
  - `route` 使用 `NC01`；
  - `work_dir` 使用唯一 `/tmp/<task-id>`；
  - worker、client、task queue 和 workflow ID 均为一次性；
  - 使用官方 Temporal SDK；
  - 输出只保留 namespace、task queue、workflow ID、预期结果和 worker stop 状态；
  - 成功或失败后幂等清理整个实验目录。
- 先动态解析 native 连接地址：

```bash
trans NC01:k3s kubectl -n temporal get service temporal-frontend \
  -o jsonpath={.spec.clusterIP}:{.spec.ports[0].port}
```

- smoke 必须完成以下闭环：
  - native worker 连接当前 Service ClusterIP；
  - client 在 `unidesk` namespace 提交唯一 workflow；
  - workflow 调用至少一个 activity；
  - client 校验精确返回值；
  - worker 明确进入 `STOPPED`；
  - 删除临时依赖、脚本、日志和 PID 文件。
- NC01 `/tmp` 是独立 tmpfs；遇到 `ENOSPC` 时加载 `$unidesk-gc`，先区分根盘与 `/tmp`，不得直接清理 k3s、containerd、PVC 或并行任务目录。

## Temporal 应用的 Native-first 闭环

- 使用 `$unidesk-devlevel` 表达应用当前采用的开发方式：
  - dispatcher/function 和单个微服务内部单元测试属于 L0；
  - native API、Worker 和 Web 属于 L1；
  - 开发集群 API/Worker/Web 属于 L2；
  - 生产集群 API/Worker/Web 属于 L3；
  - 已部署到 L2/L3 的应用仍然可以反复使用 L0/L1 开发和调试。
- 应用必须共享 contracts、repository、application dispatcher 和 Temporal contracts：
  - 本地 CLI 默认直接调用 dispatcher；
  - REST adapter 只处理 HTTP envelope、状态码、鉴权和 correlation；
  - `--overapi` 只切换 transport，不复制业务实现。
- 固定开发顺序：
  1. 完成 schema、repository、dispatcher、workflow、activity、native worker 和 CLI；
  2. 只启动 native worker，用默认 CLI 完成 PostgreSQL、CRUD 和 workflow smoke；
  3. worker 闭环后实现并 native 启动 API，用同一 CLI 加 `--overapi` 验证；
  4. native 闭环后提交应用 PR，由正常 PaC/GitOps 自动交付独立 API/worker Pod；
  5. 在线上重复同一组 CLI 命令，仅增加 `--overapi`；
  6. 上线后继续保留 native worker + 本地 dispatcher CLI 快速开发入口。
- worker 和 API 必须可以独立部署、扩缩、观察和健康检查；worker 不建立公网入口。
- 应用可调事实必须位于应用 owning YAML：
  - PostgreSQL configRef 与 Secret sourceRef；
  - Temporal serviceRef、logical namespace、task queue、超时和重试；
  - native entrypoint、PID、日志和 state 目录；
  - API/worker serviceId、端口和健康检查；
  - CLI `overapiEndpoint`。
- PR merge 是正式交付的唯一触发；禁止人工 mirror sync、PipelineRun、Argo sync 或直接 apply workload。
- 生成的 PostgreSQL `DATABASE_URL` 可能包含 `&`：
  - 禁止 `source` 或 `.` 加载生成的 env 文件；
  - 使用受控 env parser 读取单个 key，并作为单个变量传给进程；
  - 不打印 DSN。
- 验收必须同时证明：
  - native worker 消费 workflow；
  - 默认 CLI 不依赖 API；
  - native API 与 `--overapi` 同合同；
  - 自动交付后的 API/worker 独立 ready；
  - 线上 workflow 被部署 worker 消费；
  - 原入口 Web/CLI smoke 通过。

## Native-first 效率证据

- 每个应用任务在同一份任务报告中记录以下观测值：
  - `nativeIterationCount`：进入正式交付前完成的 native 修改与 smoke 轮数；
  - `nativeElapsedSeconds`：首次启动 native worker 到 native API smoke 通过的墙钟时间；
  - `deliveryRunCount`：任务产生的正式 CI/CD 流水线总数，包括失败和修复轮次；
  - `pipelineElapsedSeconds`：所有正式流水线执行时间之和；
  - `deliveryElapsedSeconds`：从首次 PR merge 到最终 runtime 验收通过的墙钟时间；
  - `rolloutCount`：任务引起的应用运行面滚动次数；
  - `meteredResourceCost`：仅在执行面提供 CPU、内存、制品存储或账单计量时记录。
- 报告必须同时给出证据来源和缺失字段，不得用 PR 数量代替流水线数，也不得把 native 轮数直接视为避免的流水线数。
- 提速百分比必须有同类任务基线或受控前后对照；没有基线时，只能报告减少了哪些 CI/CD 依赖和每轮已知等待时间。
- 降本金额必须来自实际资源或账单计量；没有计量时，只能报告省略的构建、制品写入和集群对象类别。
- 复盘优先读取这一份汇总，不再逐个反查 PR、PipelineRun 和运行面记录；原始证据仍保留为可下钻引用。

## 常见故障

- server 报 dynamic config 文件不存在：
  - 检查 YAML 的 `runtime.server.dynamicConfigFilePath`；
  - 路径必须指向当前 server 镜像内实际文件。
- UI 把 `TEMPORAL_UI_PORT=tcp://...` 当整数解析：
  - 检查 server/UI Pod spec 的 `enableServiceLinks: false`；
  - 不在容器内覆盖 Kubernetes 自动环境变量。
- `/healthz` 返回 `401`：
  - Caddy sidecar 必须用互斥 `handle /healthz` 与认证 `handle`；
  - 其他路径继续启用 Basic Auth。
- native host 无法解析 `.svc.cluster.local`：
  - 只读解析当前 Service ClusterIP；
  - 不修改 `/etc/hosts`，不增加 NodePort 或公网 gRPC。

## 跟踪与配合

- 基础设施可靠性任务统一记录在 TaskTree；遗留 MDTODO 按 `$unidesk-tasktree` 迁移。
- 详细方案、偏差与验收证据写入对应 GitHub issue，并使用 `$unidesk-gh` 受控入口。
- 跨 host 调试与临时 native smoke 使用 `$unidesk-daddev` 和 `$unidesk-trans`。
- Web 公网验收使用 `$unidesk-webdev`。
- 统一管理员密码生命周期使用 `$unidesk-secret`。
