---
name: unidesk-ymalops
description: UniDesk YAML-first 运维正规化技能。用户提到 ymal-first/YAML-first 正规化、YAML ops、运维配置职责拆分、configRef/path 引用、platform-infra 配置重构、Secret sourceRef、publicExposure、target/lane/node、ops helper 抽取、删除 hardcoded defaults/特例，或历史收敛 issue pikasTech/unidesk#390/#398/#401 时使用。
---

# UniDesk YAML Ops

## Scope

本技能用于推进 UniDesk 的 `ymal-first 正规化`：把运维参数、节点/lane/service 归属、公开暴露、Secret 绑定、容量/版本/endpoint 等可调事实收敛到 YAML；代码只负责读取、校验形状、渲染计划、执行薄的 domain CLI。

历史收敛 issue `pikasTech/unidesk#390`、`#398` 和 `#401` 都是已关闭的阶段性收口记录，不是继续追加 Round 的长期队列。后续 YAML-first 工作必须先基于当前代码重新盘点；如果需要 broad cleanup，重新创建一个边界清晰的总 issue，并在实现前冻结不超过 5 个实际代码/配置功能阶段。测试、证据收集、门禁、issue lifecycle、看板维护或 docs-only 蒸馏不能包装成阶段。

## Required Reading

开始前先读当前 workspace 的 `AGENTS.md`，并至少读：

- `docs/reference/yaml-first-ops.md`
- `docs/reference/platform-infra.md`，仅当任务涉及 `platform-infra`、Sub2API、FRP/Caddy、Codex pool 或平台基础设施服务时

如果要修改 `docs/reference/*.md`，必须同时加载并遵循 `docs-spec`，不要凭临时经验改长期参考。

## Guardrails

- 先执行 `git status --short --branch` 和 `git pull --ff-only origin master`；保留并绕开无关并行修改。
- 源码、配置、部署类正规化默认在独立 `.worktree/<task>` 中做；轻量 skill/docs/reference 收敛可按项目规则直接在主 worktree 做。
- YAML 是 source of truth。不得新增隐藏代码默认值、schema 数值硬限制、合同测试或测试硬编码策略。
- 代码校验只保证字段能被正确读取和渲染：类型、必填、枚举键名、引用存在性。版本号、namespace、endpoint、容量、冷却时间、回退窗口等数值以 YAML 为准。
- YAML 文件名、YAML 解析函数名和 YAML 渲染函数名不得携带具体 node/lane 名称或版本实例（例如 `JD01`、`D601`、`v03`、`jd01-v03`）。node/lane/version 只能作为 YAML 变量、selector、target key、template value 或 CLI 参数参与渲染；可复用文件和代码入口必须按职责命名。
- 避免“超级配置”。当一个能力同时涉及 target/lane、runtime、scenario、prompt、report、publicExposure、Secret、CI/CD 等不同职责时，按职责拆分到 owning YAML；root YAML 只保存归属和 `configRefs`/path 引用，不承载全部细节。
- 跨 YAML 引用应使用稳定的 `path/to/file.yaml#object.path` 或当前 domain parser 明确支持的等价语法。parser 只解析引用、校验存在性/类型/形状和冲突，不生成隐藏默认值，也不把合并后的大对象写成新的 source of truth。
- 新 VPS 或裸节点 bootstrap 的出网必须优先走 YAML-first 声明的 0 依赖 host proxy client：通过 `trans` 分发零依赖客户端，先让 host route 能出网，再安装 k3s、containerd、Docker、包管理器依赖和 Git/gitrepo 同步；不得把 provider-gateway WebSocket egress 当作大二进制或依赖下载的数据面。
- 新节点 node-local registry 必须支持从零部署：优先由 YAML 渲染为 k3s workload/PVC；host Docker registry 只能是 legacy/迁移前状态。需要证明从零能力时先停旧 registry，不从旧 registry seed，并在状态中披露 `seededFromOldRegistry`、`zeroSeeded`、PVC、workload、endpoint 与 runtime/tools image preload 结果。
- YAML 配置必须可复用、可继承、可组合；节点/lane 只能作为变量或 YAML value 参与渲染，禁止把具体 node/lane 写入 YAML 文件名、parser/renderer/helper 函数名或长期 schema 名。发现不同节点复制大段配置时，先抽 baseline/configRef/override，再继续扩展节点。
- CLI `plan/status` 应输出 redacted 配置引用图：每个 ref 的文件、path、presence、摘要 hash、缺失字段和下一步 drill-down 命令。不要默认 dump 展开后的完整 YAML 或 Secret。
- Secret 只能通过 YAML 的 `sourceRef`/`targetKey` 声明和受控 CLI 下发；禁止从运行面 Secret、pod env、日志或数据库状态反推、解码、回填本地凭据。
- 受控 CLI 输出只能披露对象名、key 名、sourceRef、targetKey、缺失项、fingerprint、字节数和执行摘要；不得打印 base64 payload、解码值、完整 DSN、API key 或可复制凭据。
- 不做新的全局大 orchestrator。优先保留 domain CLI，把公共能力抽到 ops helper，domain CLI 只表达领域动作。
- 不为了“防回归”新增合同测试、重型 preflight、历史 guard 或 feature flag。旧门禁阻碍最新 ymal-first 目标时，优先拆除。
- 对已经冻结范围的正规化 issue，新发现事项只能归入 `must-fix-in-current-plan`、`keep-domain-special` 或 `parked-out-of-scope`；不得因为新 grep 命中继续追加阶段，也不得把测试/证据/门禁/issue 维护当成阶段。

## Workflow

1. 盘点目标面：列出涉及的 YAML、CLI 入口、helper、Secret 绑定、运行面对象和现有 hardcode。
2. 确认归属：每个事实必须有唯一 owning YAML；代码、运行面 Secret、pod env 和 docs 不能反向成为配置真相。
3. 拆分配置职责：把不同生命周期或不同 owner 的事实拆到各自 owning YAML，用 root `configRefs`/path 引用串联；只有同 owner、同生命周期、同命令模型的字段才放在同一个对象中。
4. 归一 YAML envelope：自有运维配置优先补齐 `version`、`kind`、`metadata`、`defaults`、`targets/services/lane` 等必要结构，但不要为外部工具格式强行套壳。
5. 搬迁数值：把 namespace、serviceName、secretName、endpoint、image/tag、node/lane、probe、NO_PROXY、容量、回退窗口等可调项从代码迁到 YAML。
6. 精简 parser：parser 只做结构和类型校验，不藏业务策略，不提供长期默认值。缺项应让 CLI 报出 YAML 路径和字段名；重复声明同一事实或引用冲突时应失败并指出冲突路径。
7. 抽公共 ops primitives：在增加新 service 分支前，优先复用或扩展公共 helper。
8. 保持 domain CLI 薄：`platform-infra`、`server`、`gc`、`agentrun`、`hwlab` 等入口只组合 YAML、helper 和执行动作，不复制底层 Kubernetes/FRP/Caddy/Secret 逻辑。
9. 验证原入口：CLI/config 改动默认只跑语法、help/命令形态、plan/dry-run 或对应 sync/validate；涉及真实运行面的收口要跑原 CLI 入口，不新增合同测试。
10. 有限收口：当 issue 已经冻结阶段，完成当前阶段后只更新父 issue 的进展和下一固定阶段；固定阶段全部完成后关闭总 issue，不把候选扫描结果转成新的 Round。

## Common Refactor Targets

- `scripts/src/platform-infra-ops-library.ts`：应承载 namespace、Secret、resource apply、probe、public exposure 等跨服务 primitives。
- `scripts/src/platform-infra-public-service.ts`：应成为 FRP/Caddy/public URL 渲染的共享实现，不再让每个服务复制 writer。
- `scripts/src/pk01-caddy.ts`：PK01 Caddy/FRP 属于公共暴露能力时，应被上层 helper 调用，避免 domain CLI 直接拼配置。
- `scripts/src/secrets.ts`：保留 sourceRef/targetKey、fingerprint、presence 语义，禁止泄露 payload。
- `scripts/src/platform-db.ts`：host-native PostgreSQL 和 namespace-local bridge 是基础设施能力，不能让业务 service 自己硬编码 DSN 绑定。
- `scripts/src/hwlab-node-lanes.ts` 与 `scripts/src/agentrun-lanes.ts`：lane/node 选择应来自 YAML，不应被其他 domain 复制成特例常量。

## Special Case Policy

处理每个“特例”时先标注为以下三类之一，并在最终说明中列出：

- `keep-domain-special`：真实领域差异，保留在 owning YAML 或 domain adapter 中。
- `remove-code-default`：早期 hardcode、隐藏 fallback、重复 helper 或迁移残留，应迁到 YAML 或公共 helper。
- `legacy-retire`：历史运行面、旧门禁、旧入口或已退役路径，应删除或只保留只读迁移说明。

当前倾向保留的领域特例：

- Sub2API 的 active/standby、统一消费 key、上游账号池与 FRP 暴露关系。
- Codex pool 的 marker-only、容量、调度候选和账号状态同步。
- WeChat archive 依赖 D601 Windows bridge 的 host 现实。
- PK01 host Caddy/FRP 作为公网暴露边界。
- G14 platform PostgreSQL host-native bridge。

当前倾向消除的代码特例：

- 在代码里硬编码 `G14`、`D601`、`PK01`、`platform-infra`、`sub2api`、`sub2api-secrets` 等本应来自 YAML 的默认目标。
- `defaultTargets()`、runtime fallback、隐藏 sourceRef、隐藏 probe、隐藏 NO_PROXY 等没有 YAML 归属的默认值。
- 重复的 `asRecord`、`stringField`、`parseEnvFile`、`fingerprintValues`、compact capture、field extraction 等 helper。
- 各 service 自己拼 FRPC/Caddy/public URL/Secret apply 的实现。
- 旧 contract/preflight/guard 用历史断言限制最新 YAML-first 行为。

## Validation

- CLI 轻量变更：默认最多做 `bun --check`/TypeScript 语法、相关 `--help`、目标 `plan`/`validate`/`sync --dry-run` 或等价命令形态确认。
- 配置数值变更：只验证能被目标 CLI 读取和渲染，数值本身以 YAML 为准。
- 运行面变更：用原受控 CLI 入口验证实际对象、fingerprint、presence、health 或 public URL；不要绕到原生 `kubectl`/`curl` 作为正式控制面。
- 不做合同测试。除非用户明确要求，不新增单元测试或测试脚本。

## Closeout Format

最终回复或 issue 进展应包含：

- owning YAML 和变更模块。
- 保留的 `keep-domain-special` 特例。
- 删除或迁移的 `remove-code-default` / `legacy-retire` 特例。
- 执行过的验证命令和结果。
- 未完成阶段、剩余风险和下一步入口。
