Merge remote-tracking branch 'origin/master' into feat/2010-hwlab-release-production-lane

This commit is contained in:
Codex
2026-07-15 10:46:53 +02:00
53 changed files with 1791 additions and 44 deletions
+9 -1
View File
@@ -91,7 +91,15 @@ bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help
- 默认使用 `pipelines-as-code bootstrap --dry-run|--confirm`,不再人工串联 Gitea bootstrap 与 PaC apply
- `apply` 必须在任何 release、RBAC、Secret、Repository CR、Argo 或 webhook 写入前确认 YAML 匹配的 Gitea 仓库已存在;
- source PR 合并后不得再次调用 bootstrap 或 apply;正常验收不串联 mirror/status/history,只有失败归因才逐层下钻。
- 自动链路不通时必须修复自动链自身,并通过修复 PR 合并产生的新正常事件验收。禁止人工 mirror sync、直接 Gitea push、人工 PipelineRun、`trigger-current`、运行面 patch 或其他手段补齐当前交付;只读 status/history/events/logs/debug-step 可用于定位,但不得改变交付状态。`closeout` 只属于显式 compatibility diagnostics,不得进入默认观察或恢复路径。
- 自动链路不通时必须修复自动链自身,并通过修复 PR 合并产生的新正常事件验收
- 禁止人工 mirror sync、直接 Gitea push、人工 PipelineRun、`trigger-current`
或其他手段补齐当前交付;
- 调试或临时恢复确需改变运行面时,允许按 `$unidesk-daddev` P2 实施最小、可逆 patch
但不得修改 source/ref authority、伪造交付成功或作为最终验收;
- P2 结论必须收敛到 owning YAML、controller 或源码,
再由修复 PR 的正常自动事件交付;临时 patch 必须撤销或被声明式交付覆盖;
- 只读 status/history/events/logs/debug-step 仍是默认定位入口,
`closeout` 只属于显式 compatibility diagnostics,不得进入默认观察或恢复路径。
- CI/CD、GitOps、rollout、PipelineRun、Argo、git-mirror 和 AgentRun 的观察、诊断与 legacy 控制必须走受控 CLI;不要用裸 `kubectl``argo``tkn``curl` 当正式控制入口。PaC migrated consumer 仍只由 GitHub PR merge 触发,不得因“必须走 CLI”而额外调用写命令。
- CI/CD 校验默认不得阻塞交付:
- 除非用户明确要求某项校验成为门禁,禁止新增会让 Pipeline、artifact、GitOps promote、Argo reconcile、runtime rollout 或 `/health` closeout 失败的配置一致性、版本、契约、schema、preflight 或跨对象校验;
+98
View File
@@ -0,0 +1,98 @@
---
name: unidesk-daddev
description: UniDesk 分布式敏捷开发流程,覆盖真实运行面探测、最小可逆 patch/热补实验、YAML/Git/自动 CI/CD 持久化和原入口验收。处理跨节点、跨 host、跨 lane 的 bug、HotFix、运行面调试、临时恢复或对接 HWLAB/AgentRun/UniDesk 真实运行面时必须使用。
---
# 分布式敏捷开发流程
> 遵循规范: 本 Skill 遵循 [Skill(cli-spec)](file:///root/.agents/skills/cli-spec/SKILL.md) 规范(精简版,仅保留与流程相关的硬约束)。
## DAD-DEV SPEC
- 流程与交付
- 现场闭环
- unidesk-daddev 不是固定发布脚本,而是现场修复闭环。
- P1 先在真实 provider / host / pod / lane 上观察事实,再判断根因。
- P2 用最小、可撤、可解释的运行面实验或等价实验验证方向。
- P2 允许按调试需要实施运行面 patch 或热补:
- 只用于验证根因、缩小故障面或临时恢复用户入口;
- 开始前记录目标对象、现状、预期、影响范围和回退方式;
- 选择最小变更,避免数据迁移、Secret 反解、不可逆写入和第二 authority;
- 不得把 patch 制造的状态当作 GitOps desired、自动交付成功或最终验收证据。
- P3 把有效修复收敛回 Git、项目声明的交付路径和可审计 provenance。
- P3 必须把 P2 中成立的变更收敛到正式交付:
- 写回 owning YAML、源码或受控 renderer
- 通过正常 PR 和自动 CI/CD/GitOps 交付;
- 主动撤销临时 patch,或确认它已被声明式交付覆盖;
- 禁止长期保留隐藏运行面真相。
- P4 用用户报告的同一入口或 Web 等价 CLI 验收;单测、PR、构建和理论推导不能单独关闭 issue。
- 项目适配器
- 本 skill 只规定阶段目标、边界和证据合同。
- 分支、workspace、PR、rollout、git mirror、artifact、issue close 命令属于目标项目适配器。
- 命令细节以目标仓库当前 `AGENTS.md``docs/reference/*.md` 和 CLI help/source 为准。
- 发现 skill 示例与当前 CLI 不一致时,先修正引用或工具,不绕到原生命令。
- UniDesk `trans` / `tran` 命令遵循当前 operation 合同:
- `script``shell` operation 已移除;
- 跨 host / k3s POSIX shell 示例必须显式使用 `sh``bash`
- 检索含 Markdown 反引号的命令片段时使用单引号或 `rg -F -e`
- 禁止让本地 shell command substitution 误触旧命令。
- 固定主 repo 保护
- 执行任何会产生源码、文档、配置、issue closeout、部署脚本或验收产物的 unidesk-daddev 工作前,必须先从目标 fixed repo 的最新 remote/base 创建独立 `.worktree/<task>`,后续编辑、验证、提交、推送和受控 CLI 都在该 worktree 内执行。
- fixed repo 只用于 `git fetch``git status``git worktree add` 和读取规则;禁止把 fixed repo 当 scratch 区直接编辑,也不要把其中既有未提交修改纳入当前任务。
- fixed repo 若已有并行未提交修改,默认保持不动;用独立 worktree 隔离当前任务,除非用户明确要求合并、清理或提交这些修改。
- 只有 P1 只读探测、运行面热补或目标项目明确声明可直接改 fixed repo 的轻量例外,才允许不创建新 `.worktree`;例外必须先说明理由并避免触碰并行修改。
- 纯文档编辑可以按 `$git-spec` 的稳定分支快路径由主代理直接 commit/push,不要求独立 `.worktree` 或 PR;本地分支分叉、存在并行状态或涉及运行配置时不适用。
- Skill 自身更新边界
- 本 skill 是流程规范,不是普通任务产物;除非用户明确要求更新 `unidesk-daddev` / `SKILL.md`,不得在业务任务、文档收敛、issue closeout 或临时纠偏中自动修改本 skill。
- 发现本 skill 与当前用户要求或项目规则冲突时,先按用户要求和项目规则完成任务;需要改 skill 的事项提 issue 或请示用户,不把 skill 更新夹带进默认交付。
- 交付画像
- 需要运行面发布
- `pr-rollout`:独立 worktree → 分支 → PR → merge → CI/CD / rollout → runtime validation。
- `artifact-deploy`:提交后由 CI 产出 commit-pinned artifactCD 只消费 artifact,不从脏 worktree 构建。
- 轻量或治理交付
- `pr-lightweight`:无服务 CLI、helper、docs、config、CaseRun、trace 和治理类变更的标准画像;独立 worktree → 分支 → PR → 合并,可按项目规则自合并,合并后跳过 CI/CD/rollout。
- `config-docs-only`:无运行面 rollout 的文档/配置治理,是 `pr-lightweight` 的子类;仍需 Git/PR 证据和必要的渲染、语法或引用验证。
- 恢复后补账
- `runtime-recovery-followup`:紧急运行面恢复后,必须回补 source、runbook 或 remediation issue,不能保留隐藏 runtime truth。
- 无服务 / CI-CD SKIP
- 适用范围
- 不涉及 cloud-api、web、gateway、GitOps、k3s runtime 或其他常驻服务的改动,默认走 `pr-lightweight`:独立 worktree、分支、PR,可自合并,可跳过 CI/CD/rollout。
- 典型范围:markdown/reference/runbook/comments、CLI 工具、helper 脚本、CaseRun、harness、trace 语义化、case registry 产物整理、短连接调试 runner、无运行面影响的配置/治理。
- 交付边界
- 无服务任务必须直接运行和验证目标工具链;PR 合并后不走 CI/CD、rollout 或服务发布大回环。
- `pr-lightweight` PR 合并后即视为交付完成;关闭 issue 时写明 `rollout=not-applicable` 或等价说明。
- CaseRun 单步
- CaseRun 卡在基础设施、hwpod-node、workspace prepare、编译、下载、串口或 artifact 收集时,先拆成同一目标运行面的单步命令验证。
- 只有单步通过且需要验证完整编排、trace 和 registry 产物时,才启动一次完整 CaseRun。
- 完整 CaseRun 仍遵循 cli-spec:异步启动、短轮询、可见 trace;不得把 evidence 自动评价、门禁或自动判断重新加回流程。
- 路径与边界
- 架构收敛
- 分布式修复优先收敛到单一权威路径。
- 先识别 source of truth,再删除或降级会制造分叉的派生缓存、旧入口、兼容路径和 fallback。
- 不用多路径、fallback、feature flag、legacy mode 或额外 guard 掩盖根因。
- 已有多路径造成状态污染、可见性歧义或反复修复无效时,把读写路径统一到权威状态;其他状态只作为可重建派生证据,或直接移除。
- CLI/Web 同路径
- 定位上下文
- CLI 只能作为 Web 的非视觉等价入口,不能变成绕过 Web 的第二条业务路径。
- 先识别 Web 原入口的 `origin/lane/account/workspace/session/conversation/trace` 和实际 route / API family / dispatcher。
- 等价调用
- 正式 CLI 必须调用同一 Web origin、同一后端 dispatcher、同一账号状态和同一 session / trace 语义。
- CLI 输出必须包含 `baseUrl``route``method/path`、session/trace/job id、runtime endpoint 来源。
- 长任务用 submit-and-pollCLI 先提交,后续短查询 result / trace / inspect,不为 Web E2E 长挂单个远程命令。
- 不一致处理
- CLI 和 Web 结果不一致时,先分类为 `path-mismatch``state-mismatch``auth-mismatch``runtime-mismatch``visibility-gap`,把实际上下文写入 issue 证据,再补 CLI 同路径入口或可见性。
- 内部 direct manager、手写 dispatcher、临时 runner job、裸 API、fixture、本地 DOM 结果只能作为 canary / 定位证据,不能替代 Web 同路径验收。
- 证据与验证
- Issue 评论
- 正文先行
- Issue 评论不是机器日志归档。
- P1 进展、P2 闭环、P3 rollout、P4 closeout 和 blocker 评论,都先用自然语言说明用户现象、根因、修改、验证状态和剩余边界。
- 审计证据
- 正文之后再列命令、trace、job id、PR、PipelineRun、artifact、SHA 等证据。
- 推荐结构:2-5 段短正文说明"发生了什么 / 怎么修 / 怎么验收 / 现在状态",再用 bullets 列关键命令、trace、job、commit、rollout 耗时和产物信息。
- 阻塞说明
## 扩展参考
低频细节见 [references/details.md](references/details.md)。紧凑流程不足以支撑任务时读取该文件。
@@ -0,0 +1,206 @@
# UniDesk 分布式敏捷开发扩展参考
本文件承载从 `SKILL.md` 拆出的低频细节,保持高频路由文件紧凑。
- 阻塞评论必须讲清"为什么这是阻塞、它和当前 issue 的关系、下一步怎么解阻"。
- 不能只贴错误码、stack、JSON 摘抄或 ID 清单。
- 两层验证
- 源码层快速回归
- P1 定位根因后,默认找最小代码路径,用单元测试、合同测试、fake store、fake HTTP request、fake runtime env 等复现 bug。
- 修复后的目标行为必须固化成可重复运行的测试。
- 能 mock 的 bug 必须补源码层测试再进入 PR。
- 真实运行面验收
- CLI / Web 等价 / 原入口层必须打到目标 lane / URL / namespace / provider / device-pod / trace dispatcher,证明已发布 runtime 真的修好。
- 单元/mock 通过不能关闭 issue,关闭仍必须有 P4 原入口或真实 runtime 验收。
- 缺层说明
- 真实入口通过也不能替代可行的源码层回归测试。
- 没补源码层测试时,PR/issue 必须说明硬件物理状态、第三方 provider 非确定行为、缺少可注入边界且本次不宜扩 scope 等具体原因,并考虑最小合同、parser、env/配置选择测试或 follow-up issue。
- Closeout 必须同时写清两层证据;任一层不适用时写明原因。
## 4 阶段流程
```
┌────────────────────────────────────────────────────────────────────┐
│ P1 实地探测 → P2 最小运行面实验 → P3 持久化交付 → P4 原入口验收 │
│ SSH 透传定位根因 热补丁/替代实验 Git/项目交付收敛 用户入口复测 │
│ 只读优先,可控探针 能热补就热补 按项目适配器执行 证据落 issue │
└────────────────────────────────────────────────────────────────────┘
```
### P1:SSH 透传实地探测(只读优先,可控探针)
目标:先用真实运行面数据定位根因。结束时必须能给出"症状 + 触发路径 + 根因 + 影响面 + 期望修复方向"。
| 子步骤 | 命令 / 动作 | 退出条件 |
|---|---|---|
| 1.1 重读目标 AGENTS.md | `trans <route>:/<fixed-repo> sh -- 'cat AGENTS.md'` + 相关 `docs/reference/*` | 取得目标仓库的当前任务约束(不靠主 server 记忆) |
| 1.2 grep 关键字定位 | `rg "<symptom-keyword>" --type ts --type mjs` 等 | 找到嫌疑文件 + 行号 |
| 1.3 实测复现 | `trans <route> sh -- '<repro command>'` / 原入口 CLI / bounded logs / trace | 拿到真实 trace / error / argv,不靠理论推导 |
| 1.3b 临时探针(可选) | 只读证据不足时,最小 `apply-patch` 插入临时日志或旁路探针 | 记录目标、基线、diff、撤回方式和证据;不得混成正式修复 |
| 1.4 写根因小结 | 用 issue 原文 + 探测证据,先自然语言说明用户现象、触发路径、根因、影响面、期望修复方向和可行的单元/mock 复现点,再列关键 trace/argv/status | 可贴回 issue 评论区作为进展锚点,读者不看命令细节也能明白当前判断 |
P1 强约束:
- 证据边界
- 事实优先:严禁以"理论推导"代替实地探测;外部 API、设备、provider 的行为可能在变,必须先复现再下结论。
- 影响面:跨 lane / 跨 host 一致性问题,先在目标 lane 做最小真实闭环,再讨论通用解。
- 写入边界
- 探针:P1 默认只读;只有只读证据不足时才进入 1.3b 临时探针,且探针必须可撤、可解释、可审计。
- 修复:P1 禁止写持久化修复;临时探针不算修复完成,也不能带入 P3 正式 diff。
---
### P2:最小运行面实验(热补丁优先,可解释跳过)
目标:用最短反馈路径证明修复方向有效。能在目标运行面安全热补时,优先直接对 pod / host workspace 运行 `trans <runtime-route> apply-patch`;不能热补时,必须写清不可热补原因和替代验证路径。结束时必须能给出"实验前状态 vs 实验后状态"的可比证据。
P2 决策树:
- 热补判定
- 必须热补:问题依赖真实 runtime config / Secret / env / proxy / provider / 硬件 / k3s 对象,且目标文件或配置能被安全、可撤地热修改。
- 可跳过热补但不可跳过验证:编译型二进制、静态 bundle、schema/migration、构建产物、短连接 CLI、文档治理、测试修复或必须经 CI/CD 产物才能生效的改动。
- 跳过记录:在进展评论或 closeout 中记录 `P2 disposition=not-hotpatchable`、原因、替代证据和后续 P3/P4 验证命令。
| 子步骤 | 命令 / 动作 | 退出条件 |
|---|---|---|
| 2.1 记录运行面基线 | 记录目标 pod / host / service、source commit 或镜像版本、修复前 trace / argv / status | 后续 P3 能对齐同一基线 |
| 2.2 设计最小运行面改动或替代实验 | 找到最小的代码 / 配置 / 命令行 / 单元或合同 mock 测试改动,能证明方向 | 热补丁优先;不可热补时替代路径可解释;可 mock 的 bug 必须同时设计快速回归测试 |
| 2.3 实地热实验补丁(适用时) | 直接对目标 pod / workspace 执行 `trans <runtime-route> apply-patch` | 运行面只包含本次实验改动,可撤回 |
| 2.4 真实环境试跑 | 用目标运行面、原入口 CLI 或获批执行面直连真实 cloud-api / k3s / 设备 pod;不可热补时跑最小等价验证 | 修复路径在目标运行面或等价验证面走通 |
| 2.5 写闭环证据 | 先用正文说明实验验证了哪个修复方向、实验前后行为差异和是否可进入 P3,再列修复前/后 trace / argv / status | 能解释 P2 结论、P3 目标和是否仍需原入口复测 |
P2 强约束:
- 实验纪律
- 热补真实性:不要把需要 CI/CD 重建或镜像重推才能验证的改动伪装成热补;这类任务记录不可热补原因并转入 P3 的受控交付验证。
- 控制面冲突:热修复动作和标准 Tekton/Argo CD 打架,或被自动滚动覆盖时,创建旁路 pod 实验,不和标准 CI/CD 抢夺控制权。
- 阶段边界:P2 热补通过前禁止 commit、push、开 PR、触发 CI/CD 或把运行面热补当成正式修复;不可热补任务必须先完成 P2 disposition 说明。
- 证据纪律
- 临时改动:P2 临时改动只作为 P3 持久化交付的证据来源;pod 内 sed、临时 apply-patch、单测 mock 通过都不是修复完成证据。
- 外部依赖:第三方模型 / API / 硬件异常,必须用受控透传在真实 pod/host/provider 上复现后再下结论。
P2 推荐补丁方式:
- 入口
- 默认直接把 Codex `apply-patch` envelope 用 heredoc 投到目标运行面,不强制写临时 patch 文件。
- 示例
```bash
trans G14:k3s:hwlab-v02:hwlab-cloud-api/app apply-patch <<'PATCH'
*** Begin Patch
*** Update File: internal/cloud/example.ts
@@
old context
-old line
+new line
next context
*** End Patch
PATCH
```
- 特殊情况
- 大补丁:可以临时落文件后 `< patch.diff`,但这只是便利手段,不是流程要求。
- 路径映射:运行面路径与源码路径不一致时,P2 可以只写 runtime-specific envelopeP3 再把同一逻辑用源码路径落回 worktree,并在 issue 证据里说明路径映射和逻辑等价关系。
- 禁止绕路:不要从 host/worktree 取 `git diff`、改路径、再用复杂 shell quoting 拼到 pod;文本热修优先走 `trans <目标运行面 route> apply-patch`,第一个 route token 直接定位到目标 pod / workspace。
---
### P3:持久化交付(落回 source truth + 项目适配器)
目标:把 P2 验证有效的修复落到目标项目声明的 source truth,并通过项目适配器完成交付。结束时必须能给出可审计 provenancecommit / PR / artifact / PipelineRun / deploy job / runtime metadata 中与本次任务相符的一组证据。
P3 交付画像在上方 `DAD-DEV SPEC` 定义;3.1 必须从 `pr-rollout``pr-lightweight``artifact-deploy``config-docs-only``runtime-recovery-followup` 中选择一种,并写清选择理由。
| 子步骤 | 命令 / 动作 | 退出条件 |
|---|---|---|
| 3.1 选择交付画像 | 重读目标 `AGENTS.md` + 相关 `docs/reference/*`,确认分支、workspace、PR/CD 边界 | 明确本任务使用哪种 P3 画像和为什么 |
| 3.2 准备 source workspace | 按项目规则快进 fixed repo;从最新 remote/base 创建独立 `.worktree/<task>`,后续只在该 worktree 内工作 | base、remote、branch、worktree 路径和 fixed repo 并行修改状态可审计 |
| 3.3 整理正式修改 | 用 `apply-patch` 对源码收敛最终修改 | diff 聚焦,不带 P1/P2 临时日志、探针或旁路脚本 |
| 3.4 验证 | 跑仓库声明且与本变更相关的 check / test / smoke;对已定位 bug 优先跑新增或修改的单元 / 合同 / mock 复现测试;缺依赖按 lockfile 安装后继续 | 相关验证通过;新增回归测试能在源码层表达目标行为;预存失败或不可运行有证据和分类 |
| 3.5 提交 / PR / merge | 按交付画像执行 commit、push、PR、mergeGitHub 写走项目受控 CLI | commit / PR / merge 可追溯 |
| 3.6 受控交付 | 按项目适配器执行 CI/CD、artifact、rollout、deploy 或跳过 CI/CDpr-lightweight / config-docs-only | 不用裸 `kubectl` / `argo` / 原生 `gh` / 手写 REST 作为长期正式入口 |
| 3.7 provenance 验证 | 用项目 status/health/target validation 查看目标 commit/artifact 是否进入运行面(若适用) | runtime provenance 与本任务 source truth 对齐 |
P3 强约束:
- 入口与执行面
- 受控入口:GitHub 写、CI/CD 写、rollout 写一律走项目受控 CLI;具体命令以目标仓库当前 reference 和 CLI help/source 为准;CLI 字段不够先改 CLI 再用,不能把过期 skill 示例当成绕过理由。
- 构建边界:禁止 master server 做 build / 镜像构建;master 只做轻量源码编辑、Git 和受控 CD 观察;CI/CD 编译产物在外部 builder 跑。
- source truth
- 文本修改:源码和远端文本修改一律走 `apply-patch`(包括 `trans <route> apply-patch`);禁止用 heredoc / sed / 复杂 shell quoting 拼接大段 patch`apply-patch` 语法见 `AGENTS.md` Critical Apply Patch Syntax。
- source / worktree:禁止把落后 fixed repo 或带并行修改的 fixed repo 当 scratch 区;必须先按项目规则 fetch、状态核查,再从最新 remote/base 创建 P3 独立 `.worktree/<task>` / branch / PR;无服务任务也使用 `pr-lightweight` 的轻量 PR 形态。
- P2/P3 对账:记录 P2 热实验基线和 P3 source base;若目标分支已推进,比较中间提交是否影响触发路径;有影响则 replay 后重跑必要 P2/P4,没影响则记录 targeted revalidation 依据。
- 回归保护
- 根因能稳定抽到代码路径、配置选择、请求/响应、parser、状态机、权限或 dispatcher 合同时,P3 PR 必须包含相应单元 / 合同 / mock 测试;没有补时,PR/issue 必须说明不可行原因和替代快速拦截方案。
### 项目适配器
本 skill 不复制项目 runbook。进入 P3/P4 前,按目标项目读取适配器:
- HWLAB:以目标 HWLAB workspace 的 `AGENTS.md` 和 UniDesk `docs/reference/hwlab.md` 为准。无服务 CaseRun、短连接 CLI、trace、config、docs、helper 和配置治理类任务走 `pr-lightweight`node、lane、自动 CI/CD、target validation、public endpoint 和 device-pod closeout 只由当前项目适配器与 owning YAML 决定,不是 unidesk-daddev 通用步骤。
- AgentRun:以 `docs/reference/agentrun.md` 为准;目标 node、lane、source worktree 与自动 CI/CD authority 只从当前 owning YAML 和项目适配器解析。
- UniDesk CLI/trans/helper:以 UniDesk `AGENTS.md``docs/reference/cli.md``docs/reference/dev-environment.md` 为准。无服务 CLI、trans/tran/helper、docs、config、trace 和治理类变更走 `pr-lightweight`(独立 worktree、分支、PR,可自合并,跳过 CI/CD/rollout);业务代码、运行面、发布链路、CI/CD、Secret、权限、数据迁移、PROD 等高风险改动按对应发布画像处理。仍不得在 master server 跑仓库级 check/build/smoke。
- 其他项目:先从目标 repo 的 `AGENTS.md` 或 reference 提取:source truth、workspace、branch、交付画像、验证入口、issue close 规则和禁止动作。缺适配器时先写最小适配决策,再执行 P3。
---
### P4:交互式验收(原入口复测 + issue 关闭)
目标:用与用户最初报告的相同入口(同一 CLI、同一 device pod、同一 job id)跑一遍验收清单,把能证明修复有效的运行面证据贴回 issue,再关 issue。
| 子步骤 | 命令 / 动作 | 退出条件 |
|---|---|---|
| 4.1 准备验收清单 | 梳理 issue 里的"复现步骤 / 验收清单" | 每条都可独立跑、可独立判定 |
| 4.2 原 CLI 跑全清单 | `hwpod ...` / `trans <route> sh -- '<cli ...>'` | 每条"期望 vs 实际"都记录 |
| 4.3 关键步骤抽 trace | 抓取 job id + argv + status + blocker 全文 | argv 是修复是否生效的硬证据 |
| 4.4 写 closeout comment | 用项目受控 GitHub/issue CLI 写语义化 closeout:先用自然语言说明问题、根因、修复、验收结果和剩余边界,再列审计证据;长正文优先 `--body-file` 评论 | 读者能不解析 telemetry 就看懂结论,同时包含 PR/commit/artifact/rollout/provenance、耗时、原入口验收结果、已知未关项 |
| 4.5 关 issue | 按当前项目 CLI lifecycle 规则关闭;若 close 命令只接受短评论,先写长证据评论再用短引用关闭 | issue state=closed |
P4 强约束:
- closeout 文本
- 语义正文:开头用自然语言讲清"这个 issue 原来坏在哪里、这次根因是什么、做了什么修改、为什么现在可以认为修好了、还有什么不属于本次范围";命令、trace、job id、commit、artifact stats 放在后面的证据区,不能用证据区替代正文说明。
- provenance:不强制使用"修复前 / 修复后"配对表,也不强制所有任务列 image digest;必须列出与交付画像匹配的 provenance,例如 commit、PR、artifact、PipelineRun、deploy job、runtime metadata 或 docs/config validation。
- scopeout-of-scope 项必须显式列出(D601 host stale / COM 漂移 / 等);不能"顺手发现就修",避免 scope creep。
- 验收证据
- 两层验证:必须区分源码层单元 / 合同 / mock 回归测试和 CLI / Web 等价 / 原入口复测;缺任一层必须写明不适用原因;只跑源码层证据不能关闭 issue,必须用原入口在真实运行面跑过。
- 运行面审计:关键 argv 必须在验收报告里出现原文;关闭评论的"实际命令 / lane / URL / trace / session / job id"必须可被独立审计员复现。
- rollout 耗时:PR/CI/CD/部署滚动实际耗时必须写入 issue 评论区,至少记录起止点、总耗时、明显等待段或重跑次数;无 rollout 的任务说明 `rollout=not-applicable`
---
## 跨阶段强约束(贯穿 P1-P4)
- 变更纪律
- worktreeP3 默认先按项目规则更新 fixed repo,再从最新 remote/base 创建独立 `.worktree/<task>` / branch / PR;所有会写文件、提交、推送、issue closeout 或受控部署的动作都在该 worktree 内执行,fixed repo 只做只读预检和 worktree anchor。
- push 条件:worktree 干净且 P2 disposition 明确后才能 push;发现 P3 入口时 source commit 与 P2 运行面基线不一致,先做影响面对账。
- 并行变更:fixed repo 或其他 worktree 中的并行变更默认保持原样;当前任务用独立 `.worktree` 隔离,必要时只 cherry-pick 明确相关提交,不要 stash / 丢弃 / `git reset --hard`
- 证据纪律
- trace 落盘:issue 原始复现证据与最终验收 trace / job id / argv 要写到 issue 评论区;不要求逐条整理成配对表,但必须先有自然语言正文解释证据含义。
- rollout 记录:PR/CI/CD/部署滚动的起止时间、总耗时、重跑次数或等待段必须写到 issue 评论区;无 rollout 的任务写明不适用,并用正文解释交付路径。
- 进展锚点:用 issue 评论作进展锚点;不把 commit message、临时文件或 agent session memory 当进展锚点。
- 治理纪律
- 可见性优先:命令无输出、状态不可见、日志尾部缺失、trace 被截断或耗时不可见时,先修可见性再继续原任务;不用长日志全量 dump 代替结构化状态,优先补 CLI summary、job status、bounded tail 和 raw drill-down。
- 门禁最小化:拦截当前最新任务目标的旧测试、旧门禁、旧合同检查、旧预检、旧断言和旧 guard 一律拆除;新增 gate 只覆盖当前目标下明确且高价值的风险,优先用证据、文档共识、代码结构和标准入口自然收敛。
## 禁止行为
- 阶段错位
- 流程绕过:跳过 P1 实地探测直接改 pod 文件、跳过 P2 disposition 直接 P3 持久化、跳过 P3 持久化交付直接靠热修宣称"修好了",每次出现都强制回到 P1 重做。
- 临时当正式:pod 内热修 / 临时 apply-patch / 单测 mock 通过 ≠ 修复完成;P2 的产物是"修复需求来源"P3 的 source truth + provenance 才是"修复证据"。
- 入口绕过
- master 构建:master 是生产入口,构建会拖垮生产;任何"在 master 跑 check 通过"都不能作为有效证据。
- 原生命令写入:`kubectl apply` / 原生 `gh issue edit` / 手写 REST 写入 GitHub = 绕过项目受控 CLI,会失去 body guard、字段约束、token 轮换和审计日志;若项目允许某个 repo-owned merge path,必须在适配器里写明。
- 验证反模式
- 大回环试错:Tekton pipeline / Argo rollout 不是"兼容性探索工具";改一行 → push → 等 CI → 看结果 → 再改 → 再 push = 把 CI 当成编译器调试器用。
- 缺依赖 skip"因为 deps 没装所以跳过这个 check" = 把 noise 当 signal,必须按 lockfile 装上再验证。
- 只做 CLI 交互验收:CLI/Web 等价入口能证明真实 runtime 修好,但不能提供快速回归拦截;可 mock 的 bug 不补源码层测试就关闭 issue = 把回归风险留给用户和慢速验收。
- 边界污染
- 旧路径:旧测试、旧合同检查、旧 guard、旧 gate 与当前任务目标冲突时,不允许继续修补、加例外、旁路兼容或长期双路径;删除旧门禁后只补最小必要的新验证。
- scope creep:顺手修不在 issue scope 内的 bug、优化或命名会制造合并冲突并模糊本次修复证据;从 changeset 排除无关修改,若确认是自己刚引入的无关改动,用最小反向 patch 移除;不得回滚或删除他人并行变更。
- 适配器泛化:HWLAB v0.2 的 git mirror、AgentRun v0.1 的 control-plane、UniDesk 的 CI/CD skip 规则等只在对应项目适配器内生效,不要复制到其他项目。
- 基础设施缺陷混入业务 scopemonitor / cron / CLI 字段缺失 / 可见性不足等基础设施问题可先补最小能力解阻;长期恢复应单独 issue 跟踪,不能混入当前业务 PR。
- 证据反模式
- 无证据 close:在 issue 评论里说"应该修好了"不是证据;没有 trace / job id / argv / 截图 = 不能 close issue;证据必须是可被独立审计员复现的命令输出原文。
- telemetry dump:只有 job id、trace、commit、PipelineRun、JSON 片段而没有自然语言正文说明,读者无法理解决策;必须先写语义化正文,再列审计证据。
- session memoryagent session memory / 历史对话不可审计;证据必须落在 issue comment / git commit / PipelineRun / k8s object 这类可被外部引用的位置。
+1 -1
View File
@@ -41,7 +41,7 @@ description: >-
- 操作者不得用手写 `trans`/PowerShell/cmd 完成安装、更新、配置或自启动;
- 受控 CLI 缺失时先实现 CLI,不把手工部署当临时完成态。
- 调查可只读访问现场;发生以下变更时加载对应 skill:
- 安装、注册、更新、停止旧运行器或修改 MDTODO 来源:`$dad-dev`
- 安装、注册、更新、停止旧运行器或修改 MDTODO 来源:`$unidesk-daddev`
- 部署/发布:同时加载 `$unidesk-cicd`
- YAML 变更:同时加载 `$unidesk-ymalops`
- 远端操作走 `$unidesk-trans`。普通 `trans` 保持短连接;图形进程不得作为透传子进程长挂。
@@ -127,4 +127,4 @@ $root = Join-Path $HOME ".hwlab"
- 比较 nodeId、云端地址、资源绑定和活动硬件任务;
- 可能中断硬件操作的变更必须有明确维护窗口;
- 变更必须遵循 `$dad-dev` 流程。
- 变更必须遵循 `$unidesk-daddev` 流程。
+1 -1
View File
@@ -142,5 +142,5 @@ bun scripts/cli.ts web-probe observe status <observerId> \
- OTel trace 与跨服务 identity`$unidesk-otel`
- AgentRun durable resource/events`$unidesk-code-queue`
- Workbench 隔离 replay`$unidesk-webdev`
- 跨 host/k3s 临时诊断通道:`$unidesk-trans``$dad-dev`
- 跨 host/k3s 临时诊断通道:`$unidesk-trans``$unidesk-daddev`
- Kafka/YAML owner 或 Secret 变更:`$unidesk-ymalops`;本技能默认只读,不从运行面反解 Secret。
+1 -1
View File
@@ -20,7 +20,7 @@ description: >-
- PK01 边界:`docs/reference/pk01.md`
涉及 YAML 职责拆分时同时加载 `$unidesk-ymalops`
涉及跨 host 现场修改或验证时同时加载 `$dad-dev``$unidesk-trans`
涉及跨 host 现场修改或验证时同时加载 `$unidesk-daddev``$unidesk-trans`
## 高频入口
+1 -1
View File
@@ -40,7 +40,7 @@ description: UniDesk SelfMedia 工厂的 YAML-first 运维技能,覆盖 NC01
- Secret/YAML 归属或 renderer 修改同时使用 `$unidesk-ymalops`
- PaC、Tekton、GitOps、Argo 或自动交付排障同时使用 `$unidesk-cicd`
- 跨节点现场闭环同时使用 `$dad-dev``$unidesk-trans`
- 跨节点现场闭环同时使用 `$unidesk-daddev``$unidesk-trans`
- 公网页面、布局或浏览器原入口验收同时使用 `$unidesk-webdev`
## 详细入口
+5
View File
@@ -71,6 +71,11 @@ description: UniDesk 主代理调度子代理的必读技能。用户提到子
- 运行面故障、用户原入口不可用,或凭据/配置变更导致服务退化时,必须先恢复运行面,再完成工程化:
- 主代理先冻结恢复判定标准,并保留恢复关键路径的控制权;
- 优先用既有受控入口实施最小、可逆、可验证的恢复,只有受控入口本身缺失并直接阻塞恢复时,才在关键路径修改工具;
- 调试或临时恢复需要运行面 patch 时,按 `$unidesk-daddev` P2 执行:
- 先冻结对象、影响范围、预期与回退方式;
- patch 只用于验证或临时恢复,不得伪造 source/ref、GitOps 或交付终态;
- 最终必须写回 owning YAML、源码或 renderer,走正常 PR 与自动 CI/CD
并撤销 patch 或确认其已被声明式交付覆盖;
- 通用抽象、输出优化、skill、报告和长期治理不得成为恢复门禁;
- 恢复达到用户原入口可用的标准后,主代理不得把临时恢复当作任务完成,必须继续完成用户明确要求的根因修复、工程化、PR、验证和治理;
- 与恢复根因解耦且具备独立 issue、MDTODO、worktree 和验收入口的工程化任务,必须在恢复期间立即并行派发,不得全部排到恢复之后串行执行。
@@ -26,9 +26,13 @@
- 依赖图必须先标出运行面恢复关键路径和用户原入口恢复标准。主代理控制恢复关键路径,避免多个代理同时修改同一生产对象、恢复状态机或共享配置。
- 恢复优先指先让用户原入口通过既有受控路径恢复到可验证状态,不表示工程化任务整体串行:
- 恢复关键路径上的最小诊断、配置修正和复测按真实依赖顺序执行;
- 必要的运行面 patch 由 `$unidesk-daddev` P2 约束,只证明方向或临时恢复;
不得把 patch 状态当作 source authority、自动交付成功或最终验收;
- 与根因解耦的 CLI、reference、报告、独立仓库修复和长期治理,在具备独立 issue、MDTODO、worktree、PR 与验收入口时立即并行;
- 只有缺少受控恢复入口直接阻塞恢复时,才允许把工具修改放到恢复关键路径。
- 运行面恢复后,主代理继续完成根因修复、持久化配置、自动交付、原入口复测和治理收口;不得以临时恢复或单次手工成功代替用户要求的终态。
- P2 patch 验证成立后,主代理必须把变化收敛到 owning YAML、源码或 renderer
通过正常 PR 与自动 CI/CD/GitOps 交付,并撤销 patch 或确认其已被声明式交付覆盖。
- 恢复期间降低并发必须有具名原因,例如共享生产对象、同一状态机、权限边界或运行面容量;原因解除后立即重新计算可安全并发窗口。
## 模型与思考等级
+9 -1
View File
@@ -27,7 +27,12 @@ trans D601:win/c/test git exact-commit gc-auto --confirm
trans gh:/owner/repo/issue/<number> cat
```
Host workspace、k3s、Windows、GitHub issue/PR route 见 [references/routes.md](references/routes.md)sh/bash/argv/py/upload/download/kubectl/logs/skills/tcp-pool 操作见 [references/operations.md](references/operations.md)apply-patch envelope 和 quoting 陷阱见 [references/apply-patch.md](references/apply-patch.md)。
专项参考:
- Host workspace、k3s、Windows、GitHub issue/PR route 见 [references/routes.md](references/routes.md)。
- sh/bash/argv/py/upload/download/kubectl/logs/skills/tcp-pool 操作见 [references/operations.md](references/operations.md)。
- 跨 60 秒短连接的临时 build、GPU、模型或硬件 smoke 见 [references/remote-experiment-smoke.md](references/remote-experiment-smoke.md)。
- apply-patch envelope 和 quoting 陷阱见 [references/apply-patch.md](references/apply-patch.md)。
## P0 边界
@@ -53,6 +58,9 @@ Host workspace、k3s、Windows、GitHub issue/PR route 见 [references/routes.md
- 当前 HWLAB node/lane source workspace 的正式预检/同步入口是 `bun scripts/cli.ts hwlab nodes control-plane source-workspace sync --node <node> --lane <lane> --confirm``source-workspace status`;不要把裸 `trans <node>:<workspace> git fetch ...` 当成当前 HWLAB node/lane workspace 预检/修复入口。
- `sh`/`bash` 必须显式声明 shell;单进程命令优先 direct argv 或已知 operation。
- 普通 trans/ssh 短连接硬预算 60s;长 CI/CD、trace、logs、build、硬件流程必须 submit-and-poll。
- 一次性远程实验不得临时发明第二套状态协议:
- 使用 [远程临时实验 smoke](references/remote-experiment-smoke.md) 的参数合同;
- 复用同一模板的后台 job、证据和清理合同。
- `/mnt/<drive>` WSL host workspace 由 root 透传执行时,CLI 自动把 workspace owner uid 桥接到进程级 `SUDO_UID`,让 Git 信任 Windows 挂载目录而不写全局 `safe.directory`;非 Git 命令和普通 Linux workspace 不改变所有权信任。
- Windows route 的 `win` 是 route planeoperation 直接写 `ps``cmd``git` 或只读 fs 操作 `pwd|ls|cat|head|tail|stat|wc|rg`;不要写成 `trans D601:win/... win ps`,也不要把 POSIX shell 当 Windows shell。
- Windows `rg`
@@ -31,3 +31,8 @@
- 验证独立 worktree 时显式设置 `UNIDESK_TRANS_REPO_ROOT=<worktree>`
- 普通 trans/SSH 必须满足短连接预算;长 build、CI/CD、trace、probe 或 rollout 使用受控 job/status submit-and-poll。
- 临时 build、Docker GPU、模型下载或硬件 smoke
- 使用 [远程临时实验 smoke](remote-experiment-smoke.md) 的单次参数化模板;
- 长步骤只提交后台 job,后续使用有界 status/log tail 轮询;
- 结束时输出统一 evidence 摘要,并复查资源前缀或 label 无残留。
@@ -0,0 +1,194 @@
# 远程临时实验 Smoke
## 适用范围
本模板用于一次性远程 build、Docker GPU、模型下载或硬件 smoke。目标是让执行者在一个实验目录内完成:
- 一次 preflight
- 一个或多个串行后台 job
- 一次原入口 smoke
- 一份有界 evidence
- 一次幂等 cleanup。
正式 CI/CD、YAML-first apply、常驻服务、Secret 下发和公网暴露继续使用对应专项 CLI 或 skill,不能套用本模板。
## 必填参数
开始写入目标前必须明确以下参数,不得根据宿主名、历史目录或上一任务猜测:
| 参数 | 约束 |
| --- | --- |
| `route` | 任务上下文提供的 `Target:absoluteWorkspace` 或对应 k3s route。 |
| `work_dir` | 目标允许范围内的绝对临时目录,默认位于 `/tmp`。 |
| `resource_prefix` | 容器、镜像、网络、PID 和证据文件共享的任务前缀。 |
| `job_id` | 当前长步骤的稳定短标识,例如 `model-download``image-build`。 |
| `command` | 写入 job 脚本的单一长命令,不在同步 `trans` 中等待完成。 |
| `artifact_allowlist` | 允许保留的模型缓存、WAV、JSON 或摘要文件。 |
| `stop_condition` | OOM、ABI、许可证、上游 ref 或硬件能力等首次确定停止条件。 |
参数缺失时先返回可操作错误:
- 不要创建第二条执行路径;
- 不要把 Secret、token 或完整环境变量写入参数、脚本、日志和 evidence。
## 单次 Smoke 流程
### 1. Preflight
只做一次有界探测,确认目录、资源前缀、依赖入口和停止条件:
```bash
trans <Target>:/tmp sh <<'SH'
set -eu
work_dir=<absolute-work-dir>
resource_prefix=<resource-prefix>
test "${work_dir#/tmp/}" != "$work_dir"
case "$resource_prefix" in
''|*[!a-zA-Z0-9_.-]*) echo 'invalid resource_prefix' >&2; exit 2 ;;
esac
mkdir -p "$work_dir/jobs" "$work_dir/artifacts"
printf 'host='; hostname
df -h "$work_dir" | tail -1
docker version --format 'docker={{.Server.Version}}' 2>/dev/null || true
nvidia-smi --query-gpu=name,driver_version,memory.total,memory.free --format=csv,noheader 2>/dev/null || true
docker ps -a --filter "label=unidesk.task=$resource_prefix" --format '{{.Names}} {{.Status}} {{.Image}}' 2>/dev/null || true
SH
```
若目标范围不是 `/tmp`,必须由 owning issue 或任务上下文明确授权,再相应替换目录断言。
### 2. 写入 Job 脚本
远端文本修改只使用 `apply-patch`。每个长步骤的脚本必须写最终 exit code 和 done 标记:
```bash
trans <Target>:<absolute-work-dir> apply-patch <<'PATCH'
*** Begin Patch
*** Add File: jobs/<job-id>.sh
+#!/bin/sh
+set +e
+
+<one-long-command>
+code=$?
+printf '%s\n' "$code" > /absolute/work-dir/jobs/<job-id>.exit
+touch /absolute/work-dir/jobs/<job-id>.done
+exit "$code"
*** End Patch
PATCH
```
job 脚本只能引用任务已确认的绝对目录、资源前缀和公开依赖身份。需要 Secret 的任务不使用本模板自行读取运行面值。
### 3. Submit
submit 只负责清理旧状态、后台启动和返回 PID,不等待长命令:
```bash
trans <Target>:<absolute-work-dir> sh <<'SH'
set -eu
job_id=<job-id>
job_dir=/absolute/work-dir/jobs
rm -f "$job_dir/$job_id.done" "$job_dir/$job_id.exit" "$job_dir/$job_id.stdout" "$job_dir/$job_id.stderr"
chmod +x "$job_dir/$job_id.sh"
nohup sh "$job_dir/$job_id.sh" \
> "$job_dir/$job_id.stdout" \
2> "$job_dir/$job_id.stderr" \
< /dev/null &
printf '%s\n' "$!" > "$job_dir/$job_id.pid"
printf 'job=%s state=submitted pid=%s\n' "$job_id" "$(cat "$job_dir/$job_id.pid")"
SH
```
### 4. Poll
每次 poll 只输出状态和有界日志尾部。不得重复 submit,也不得恢复完整 stdout dump
```bash
trans <Target>:<absolute-work-dir> sh <<'SH'
set -eu
job_id=<job-id>
job_dir=/absolute/work-dir/jobs
pid=$(cat "$job_dir/$job_id.pid" 2>/dev/null || true)
if [ -f "$job_dir/$job_id.done" ]; then
state=finished
exit_code=$(cat "$job_dir/$job_id.exit")
elif [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
state=running
exit_code=-
else
state=lost
exit_code=-
fi
printf 'job=%s state=%s exit=%s pid=%s\n' "$job_id" "$state" "$exit_code" "${pid:--}"
printf '%s\n' '--- stdout tail ---'
tail -n 20 "$job_dir/$job_id.stdout" 2>/dev/null || true
printf '%s\n' '--- stderr tail ---'
tail -n 20 "$job_dir/$job_id.stderr" 2>/dev/null || true
SH
```
`state=lost` 表示 PID 已消失但没有 done 标记:
- 先保留状态文件和日志;
- 不要自动重提;
- 根据首个确定错误决定停止或创建新的 `job_id`
### 5. 原入口 Smoke 与 Evidence
只执行 owning issue 要求的一个 smoke。通用 evidence 至少包含:
- `sourceRef`、模型或依赖 ref、许可证;
- 容器基础镜像 digest、构建镜像 ID、runtime 和 GPU request
- 精确 API 或命令参数;
- 首次调用和热调用耗时;
- GPU 基线、峰值显存和 OOM 状态;
- 生成物大小、哈希和原生解码/检查摘要;
- 首个失败点和是否触发 `stop_condition`
- cleanup 结果和允许保留的 artifact 路径。
证据文件只写摘要、presence、fingerprint、ID、digest 和有界日志尾部,不复制 Secret、完整环境、模型正文或无界 stdout。
### 6. Cleanup
cleanup 必须幂等,并按资源前缀或 label 精确删除:
```bash
trans <Target>:<absolute-work-dir> sh <<'SH'
set -eu
work_dir=/absolute/work-dir
resource_prefix=<resource-prefix>
for container in $(docker ps -aq --filter "label=unidesk.task=$resource_prefix"); do
docker rm -f "$container"
done
for image in $(docker images -q --filter "label=unidesk.task=$resource_prefix"); do
docker image rm "$image" || true
done
for network in $(docker network ls -q --filter "label=unidesk.task=$resource_prefix"); do
docker network rm "$network" || true
done
find "$work_dir/jobs" -type f \( -name '*.pid' -o -name '*.done' -o -name '*.exit' \) -delete
printf '%s\n' 'containers:'
docker ps -a --filter "label=unidesk.task=$resource_prefix" --format '{{.Names}} {{.Status}} {{.Image}}'
printf '%s\n' 'images:'
docker images --filter "label=unidesk.task=$resource_prefix" --format '{{.Repository}}:{{.Tag}} {{.ID}} {{.Size}}'
printf '%s\n' 'networks:'
docker network ls --filter "label=unidesk.task=$resource_prefix" --format '{{.Name}}'
SH
```
只保留 `artifact_allowlist` 明确列出的缓存和证据。临时容器、镜像、网络、PID 和非复用 scratch 不得伪装成正式运行面。
## 停止判定
- 首个确定的 OOM、ABI、许可证、上游 ref 或硬件能力错误触发 `stop_condition` 后立即停止同族试配。
- 依赖下载失败与模型失败必须分层记录,不能把网络、registry 或包 ABI 问题误判为模型不可用。
- smoke 未进入原入口时,首次调用、热调用、峰值显存和生成物必须明确写 `未执行`,不能填推测值。
- 后续候选模型或依赖组合使用新的 issue、MDTODO、资源前缀和实验目录,不能在当前 smoke 中无界扩展。
+5
View File
@@ -28,6 +28,11 @@ description: UniDesk YAML-first 运维正规化技能。用户提到 ymal-first/
- 先执行 `git status --short --branch``git pull --ff-only origin master`;保留并绕开无关并行修改。
- 源码、配置、部署类正规化默认在独立 `.worktree/<task>` 中做;轻量 skill/docs/reference 收敛可按项目规则直接在主 worktree 做。
- YAML 是 source of truth。不得新增隐藏代码默认值、schema 数值硬限制、合同测试或测试硬编码策略。
- `$unidesk-daddev` P2 调试例外不改变 YAML-first 终态:
- 可对运行面做最小、可逆 patch 以验证根因或临时恢复;
- patch 不得反向成为配置真相、第二 authority 或自动交付成功证据;
- 验证成立后必须把变化收敛到 owning YAML、parser/renderer 或源码,
经正常 PR 与自动 CI/CD/GitOps 交付,并撤销 patch 或确认其已被声明式交付覆盖。
- 代码校验只保证字段能被正确读取和渲染:类型、必填、枚举键名、引用存在性。版本号、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 YAMLroot YAML 只保存归属和 `configRefs`/path 引用,不承载全部细节。
+5 -1
View File
@@ -83,7 +83,7 @@
## P0: 受控入口与技能路由
- P0: 跨 host/lane 现场修复加载 `$dad-dev`
- P0: 跨 host/lane 现场修复加载 `$unidesk-daddev`
- 完成真实运行面探测;
- 完成最小验证、持久化和原入口复测。
- P0: CI/CD、GitOps、rollout、PipelineRun、Argo、git-mirror 和 AgentRun 部署:
@@ -126,6 +126,10 @@
- 透传路由:`G14-WSL:win/d/Work/CONSTAR_workspace`
- Windows 路径:`D:\Work\CONSTAR_workspace`
- 直接在该固定主 worktree 工作,不创建任务 `.worktree`;并行改动保护和精确提交边界见 `docs/reference/devops-hygiene.md`
- P0: MET 非线性补偿研究与 MN 小论文投稿查询固定工作区:
- 透传路由:`D518:win/c/work/met_nonlinear_master`
- Windows 路径:`C:\work\met_nonlinear_master`
- 投稿任务入口:`docs/MDTODO/20260605-MN投稿前修改.md`
- P0: HWLAB 凭据脱敏和 issue 关闭遵循 `docs/reference/hwlab.md`
- 关闭前必须在选中 node/lane 走原入口真实验证;
- 源码、PR 或构建通过不能替代原入口验证。
+517
View File
@@ -496,6 +496,523 @@ controlPlane:
gitopsBranch: nc01-v0.2-gitops
gitopsPath: deploy/gitops/node/nc01/runtime-v02
argoApplication: agentrun-nc01-v02
nc01-release:
deployment:
format: unidesk-yaml-only
gitopsRoot: deploy/gitops/node/nc01
runtimeRenderDir: runtime-release
artifactCatalogPath: deploy/artifact-catalog.nc01-release.json
argocd:
project: agentrun-nc01-release
applicationFile: application-release.yaml
manager:
serviceAccount: agentrun-nc01-release-mgr
apiKeySecretRef:
name: agentrun-release-api-key
key: HWLAB_API_KEY
env:
AGENTRUN_POSTGRES_POOL_MAX: '4'
AGENTRUN_MANAGER_RECONCILER_BATCH_SIZE: '20'
AGENTRUN_MANAGER_RECONCILER_ENABLED: 'true'
AGENTRUN_MANAGER_RECONCILER_INTERVAL_MS: '30000'
AGENTRUN_RUNNER_DISPATCHER_ENABLED: 'true'
AGENTRUN_RUNNER_DISPATCHER_INTERVAL_MS: '1000'
AGENTRUN_RUNNER_DISPATCHER_BATCH_SIZE: '10'
AGENTRUN_RUNNER_DISPATCHER_LEASE_MS: '120000'
AGENTRUN_RUNNER_DISPATCHER_ATTEMPT_TIMEOUT_MS: '90000'
AGENTRUN_RUNNER_DISPATCHER_MAX_ATTEMPTS: '5'
AGENTRUN_RUNNER_DISPATCHER_RETRY_BACKOFF_MS: '5000'
AGENTRUN_KAFKA_ENABLED: 'true'
AGENTRUN_KAFKA_OUTBOX_INTERVAL_MS: '500'
AGENTRUN_KAFKA_OUTBOX_BATCH_SIZE: '100'
AGENTRUN_KAFKA_OUTBOX_LEASE_MS: '60000'
AGENTRUN_KAFKA_OUTBOX_RETRY_BACKOFF_MS: '5000'
AGENTRUN_KAFKA_STDIO_PRODUCE_ENABLED: 'true'
AGENTRUN_KAFKA_BOOTSTRAP_SERVERS: platform-infra-kafka-kafka-bootstrap.platform-infra.svc.cluster.local:9092
AGENTRUN_KAFKA_EVENT_TOPIC: agentrun.event.v1
AGENTRUN_KAFKA_STDIO_TOPIC: codex-stdio.raw.v1
AGENTRUN_KAFKA_CLIENT_ID: agentrun-release-manager
AGENTRUN_KAFKA_STDIO_CLIENT_ID: agentrun-release-runner
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: http://otel-collector.platform-infra.svc.cluster.local:4318/v1/traces
OTEL_SERVICE_NAME: agentrun-manager
UNIDESK_NODE_ID: NC01
HWLAB_RUNTIME_LANE: release
unideskSshEndpointEnv:
name: UNIDESK_MAIN_SERVER_IP
value: 152.53.229.148
bootRepoUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
imageBuild:
context: .
containerfile: deploy/container/Containerfile
repository: agentrun-mgr-env
network: host
buildArgs:
BUN_IMAGE: oven/bun:1-alpine
httpProxy: http://127.0.0.1:10808
httpsProxy: http://127.0.0.1:10808
noProxy:
- localhost
- 127.0.0.1
- ::1
- 127.0.0.1:5000
- localhost:5000
- .svc
- .svc.cluster.local
- .cluster.local
- hyueapi.com
- .hyueapi.com
buildContainerProxy:
httpProxy: http://127.0.0.1:10808
httpsProxy: http://127.0.0.1:10808
noProxy:
- localhost
- 127.0.0.1
- ::1
- 127.0.0.1:5000
- localhost:5000
- .svc
- .svc.cluster.local
- .cluster.local
- hyueapi.com
- .hyueapi.com
envIdentityFiles:
- deploy/container/Containerfile
- deploy/runtime/boot/agentrun-boot.sh
- deploy/runtime/boot/agentrun-mgr.sh
- deploy/runtime/boot/agentrun-runner.sh
- src
- scripts
- package.json
- bun.lock
- tsconfig.json
timeoutSeconds: 1800
pollSeconds: 15
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 800m
memory: 1Gi
runner:
serviceAccount: agentrun-nc01-release-runner
jobNamePrefix: agentrun-nc01-release-runner
idleTimeoutMs: 600000
backendRetry:
maxAttempts: 5
initialBackoffMs: 1000
maxBackoffMs: 30000
apiKeySecretRef:
name: agentrun-release-api-key
key: HWLAB_API_KEY
egressProxyUrl: http://10.42.0.1:10808
noProxyExtra:
- localhost
- 127.0.0.1
- ::1
- .svc
- .svc.cluster.local
- .cluster.local
- hyueapi.com
- .hyueapi.com
- api.pikapython.com
- .pikapython.com
retention:
maxRunners: 10
cleanupOrder: oldest-inactive-last-active-first
activeHeartbeatMaxAgeMs: 900000
stalePendingMaxAgeMs: 900000
selectors:
matchLabels:
app.kubernetes.io/part-of: agentrun
app.kubernetes.io/name: agentrun-runner
app.kubernetes.io/component: runner
jobNamePrefixes:
- agentrun-nc01-release-runner
- agentrun-release-runner
ageBasedCleanup:
enabled: false
maxAgeHours: 48
sessionPvcRetention:
enabled: true
prefixes:
- agentrun-release-session-
- agentrun-nc01-release-session-
maxDeletePerRun: 1000
cancelLifecycle:
deliveryMode: manager-epoch
gracefulAbortMs: 15000
killEscalationMs: 30000
staleHeartbeatFencingMs: 900000
lateWriteFencing:
enabled: true
eventStages:
- accepted
- persisted
- delivered
- aborting
- terminalized
- fenced
- late-write-rejected
localPostgres:
enabled: false
serviceName: agentrun-release-postgres
image: postgres:16-alpine
storage: 5Gi
port: 5432
database: agentrun_release
user: agentrun_release
passwordSourceRef: agentrun/nc01-release-local-postgres.env
passwordSourceKey: POSTGRES_PASSWORD
gitMirror:
namespace: devops-infra
readService: git-mirror-http
readDeployment: git-mirror-http
writeService: git-mirror-write
writeDeployment: git-mirror-write
resourceBundleBaseUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080
readUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
writeUrl: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
cachePvc: hwlab-git-mirror-cache
cacheHostPath: null
sshSecretName: git-mirror-github-ssh
githubProxy:
host: 127.0.0.1
port: 10808
toolsImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
syncJobPrefix: git-mirror-agentrun-nc01-release-sync-manual
flushJobPrefix: git-mirror-agentrun-nc01-release-flush-manual
repositoryReconciler:
enabled: true
deploymentName: agentrun-nc01-release-managed-repository-reconciler
configMapName: agentrun-nc01-release-managed-repository-reconciler
serviceAccountName: agentrun-nc01-release-managed-repository-reconciler
remoteAuth:
configRef: config/platform-infra/gitea.yaml#sourceAuthority.credentials.github.gitFetchCredential
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
imagePullPolicy: IfNotPresent
cacheMountPath: /cache
stateDirectory: .agentrun-managed-repositories/nc01-release
reconcileIntervalMs: 30000
fetchTimeoutMs: 240000
shutdownGraceMs: 30000
maxConcurrentRepositories: 2
retry:
maxAttempts: 4
initialDelayMs: 1000
maxDelayMs: 15000
freshness:
maxAgeMs: 180000
readiness:
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
status:
defaultRepositoryLimit: 8
lifecycle:
undeclaredRepositoryPolicy: retain
managedRefs: source-branch-only
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
cpu: 500m
memory: 256Mi
repositories:
- key: agentrun
repository: pikasTech/agentrun
remote: https://github.com/pikasTech/agentrun.git
sourceBranch: release
gitopsBranch: nc01-release-gitops
- key: unidesk
repository: pikasTech/unidesk
remote: https://github.com/pikasTech/unidesk.git
sourceBranch: master
- key: agent_skills
repository: pikasTech/agent_skills
remote: https://github.com/pikasTech/agent_skills.git
sourceBranch: master
database:
mode: external-postgres
provider: NC01
configRef: config/platform-db/postgres-nc01.yaml
database: agentrun_release
user: agentrun_release
sslmode: require
secretSourceRef: agentrun/nc01-release-mgr-db.env
secretRef:
name: agentrun-release-mgr-db
key: DATABASE_URL
localPostgresExpectedAbsent: true
toolCredentials:
- id: github-pr
tool: github
purpose: github-pr
secretRef:
namespace: agentrun-release
name: agentrun-release-tool-github-pr
keys:
- GH_TOKEN
projection:
kind: env
envName: GH_TOKEN
secretKey: GH_TOKEN
- id: github-repository-override-pikaoa
tool: github
purpose: repository-override-pikainc-pikaoa
secretRef:
namespace: agentrun-release
name: agentrun-release-tool-github-repository-overrides
keys:
- PIKAINC_PIKAOA_GH_TOKEN
projection:
kind: env
envName: UNIDESK_GH_TOKEN_PIKAINC_PIKAOA
secretKey: PIKAINC_PIKAOA_GH_TOKEN
- id: unidesk-ssh
tool: unidesk-ssh
purpose: ssh-passthrough
secretRef:
namespace: agentrun-release
name: agentrun-release-tool-unidesk-ssh
keys:
- UNIDESK_SSH_CLIENT_TOKEN
projection:
kind: env
envName: UNIDESK_SSH_CLIENT_TOKEN
secretKey: UNIDESK_SSH_CLIENT_TOKEN
- id: github-ssh
tool: github
purpose: github-ssh
secretRef:
namespace: agentrun-release
name: agentrun-release-tool-github-ssh
keys:
- id_ed25519
- known_hosts
projection:
kind: volume
mountPath: /home/agentrun/.ssh
secrets:
- id: manager-api-key
sourceRef: hwlab/nc01-v03-admin.env
sourceKey: HWLAB_API_KEY
targetRef:
namespace: agentrun-release
name: agentrun-release-api-key
key: HWLAB_API_KEY
- id: provider-codex-auth-json
sourceMode: file
sourceRef: /root/.codex/auth.json
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-codex
key: auth.json
providerCredential:
profile: codex
- id: provider-codex-config
sourceMode: file
sourceRef: /root/.codex/config.toml
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-codex
key: config.toml
providerCredential:
profile: codex
- id: provider-gpt-pika-auth-json
sourceMode: file
sourceRef: /root/.codex/auth.json.pika
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-gpt-pika
key: auth.json
providerCredential:
profile: gpt-pika
- id: provider-gpt-pika-config
sourceMode: file
sourceRef: /root/.codex/config.toml.pika
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-gpt-pika
key: config.toml
providerCredential:
profile: gpt-pika
- id: provider-grok-auth-json
sourceMode: file
sourceRef: /root/.codex/auth.json.grok
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-grok
key: auth.json
providerCredential:
profile: grok
- id: provider-grok-config
sourceMode: file
sourceRef: /root/.codex/config.toml.grok
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-grok
key: config.toml
providerCredential:
profile: grok
- id: provider-dsflash-go-auth-json
sourceMode: env
sourceRef: hwlab/nc01-v03-code-agent-provider.env
sourceKey: OPENCODE_API_KEY
transform: codex-auth-json-openai-api-key
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-dsflash-go
key: auth.json
providerCredential:
profile: dsflash-go
- id: provider-dsflash-go-config
sourceMode: codex-config
sourceRef: config/agentrun.yaml#controlPlane.lanes.nc01-release.secrets.provider-dsflash-go-config.codexConfig
codexConfig:
modelProvider: opencode
model: deepseek-v4-flash
reviewModel: deepseek-v4-flash
modelReasoningEffort: xhigh
disableResponseStorage: true
networkAccess: enabled
modelContextWindow: 1000000
modelAutoCompactTokenLimit: 900000
modelCatalogJson: /home/agentrun/.codex-dsflash-go/model-catalog.json
approvalsReviewer: user
modelProviders:
opencode:
name: Moon Bridge OpenCode Zen Go Flash
baseUrl: http://hwlab-deepseek-proxy.hwlab-v03.svc.cluster.local:4000/v1
wireApi: responses
requiresOpenaiAuth: true
projects:
/root:
trustLevel: trusted
/home/agentrun/workspaces:
trustLevel: trusted
tuiModelAvailabilityNux:
deepseek-v4-flash: 4
noticeHideFullAccessWarning: true
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-dsflash-go
key: config.toml
providerCredential:
profile: dsflash-go
- id: provider-dsflash-go-model-catalog
sourceMode: json
sourceRef: config/agentrun.yaml#controlPlane.lanes.nc01-release.secrets.provider-dsflash-go-model-catalog.jsonValue
jsonValue:
models:
- slug: deepseek-v4-flash
display_name: DeepSeek V4 Flash via OpenCode Zen Go
description: DeepSeek V4 Flash exposed to Codex through OpenCode Zen Go and a Responses-compatible Moon Bridge profile.
default_reasoning_level: xhigh
supported_reasoning_levels:
- effort: low
description: Fast responses with lighter reasoning
- effort: medium
description: Balanced reasoning
- effort: high
description: Deep reasoning
- effort: xhigh
description: Maximum reasoning
shell_type: shell_command
visibility: list
supported_in_api: true
priority: 0
additional_speed_tiers: []
service_tiers: []
availability_nux: null
upgrade: null
base_instructions: You are Codex, a coding agent based on DeepSeek V4 Flash. You and the user share one workspace, and your job is
to collaborate with them until their goal is genuinely handled.
model_messages: {}
supports_reasoning_summaries: true
default_reasoning_summary: auto
support_verbosity: false
apply_patch_tool_type: freeform
web_search_tool_type: text_and_image
truncation_policy:
mode: tokens
limit: 10000
supports_parallel_tool_calls: true
supports_image_detail_original: false
context_window: 1000000
max_context_window: 1000000
auto_compact_token_limit: 900000
effective_context_window_percent: 95
experimental_supported_tools: []
input_modalities:
- text
supports_search_tool: false
targetRef:
namespace: agentrun-release
name: agentrun-release-provider-dsflash-go
key: model-catalog.json
providerCredential:
profile: dsflash-go
- extends: controlPlane.templates.secrets.githubPrRawToken
targetRef:
namespace: agentrun-release
name: agentrun-release-tool-github-pr
key: GH_TOKEN
- extends: controlPlane.templates.secrets.githubRepositoryOverridePikaoaToken
targetRef:
namespace: agentrun-release
name: agentrun-release-tool-github-repository-overrides
key: PIKAINC_PIKAOA_GH_TOKEN
- extends: controlPlane.templates.secrets.unideskSshToken
targetRef:
namespace: agentrun-release
name: agentrun-release-tool-unidesk-ssh
key: UNIDESK_SSH_CLIENT_TOKEN
- extends: controlPlane.templates.secrets.githubSshPrivateKey
targetRef:
namespace: agentrun-release
name: agentrun-release-tool-github-ssh
key: id_ed25519
- extends: controlPlane.templates.secrets.githubSshKnownHosts
targetRef:
namespace: agentrun-release
name: agentrun-release-tool-github-ssh
key: known_hosts
extends: controlPlane.templates.agentrunV02Lane
variables:
NODE: NC01
remote: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
workspace: /root/agentrun-release
pipeline: agentrun-nc01-release-ci-image-publish
pipelineRunPrefix: agentrun-nc01-release-ci
serviceAccountName: agentrun-nc01-release-tekton-runner
gitopsBranch: nc01-release-gitops
gitopsPath: deploy/gitops/node/nc01/runtime-release
argoApplication: agentrun-nc01-release
overrides:
version: release
source:
branch: release
bootstrapFromBranch: v0.2
workspace: /root/agentrun-release
runtime:
namespace: agentrun-release
managerDeployment: agentrun-mgr
managerService: agentrun-mgr
managerPort: 8080
internalBaseUrl: http://agentrun-mgr.agentrun-release.svc.cluster.local:8080
ci:
pipeline: agentrun-nc01-release-ci-image-publish
pipelineRunPrefix: agentrun-nc01-release-ci
serviceAccountName: agentrun-nc01-release-tekton-runner
gitops:
branch: nc01-release-gitops
path: deploy/gitops/node/nc01/runtime-release
argoApplication: agentrun-nc01-release
templates:
agentrunV02Lane:
version: v0.2
+21
View File
@@ -244,6 +244,27 @@ sourceAuthority:
readUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
configRef: config/cicd-branch-followers.yaml#followers.agentrun-nc01-v02.nativeStatus.source.gitMirrorReadUrl
disposition: replaced-by-gitea
- key: agentrun-nc01-release
targetId: NC01
upstream:
repository: pikasTech/agentrun
cloneUrl: https://github.com/pikasTech/agentrun.git
branch: release
gitea:
owner: mirrors
name: pikasTech-agentrun
mirrorMode: controlled-push
publicRead: true
readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
gitops:
branch: nc01-release-gitops
flushDisposition: retained-for-gitops-flush
snapshot:
naming: agentrun-release-prefix
prefix: refs/unidesk/snapshots/gitea-actions/agentrun-release
legacyGitMirror:
readUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
disposition: replaced-by-gitea
- key: unidesk-master
targetId: JD01
upstream:
@@ -42,6 +42,37 @@ release:
controllerServiceName: pipelines-as-code-controller
controllerServicePort: 8080
waitTimeoutSeconds: 55
workloads:
watcher:
deploymentName: pipelines-as-code-watcher
containerName: pac-watcher
startupProbe:
httpGet:
path: /live
port: probes
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 60
successThreshold: 1
readinessProbe:
httpGet:
path: /live
port: probes
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
livenessProbe:
httpGet:
path: /live
port: probes
initialDelaySeconds: 180
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
deliveryProvenance:
version: admission-pac-v2
markerAnnotation: unidesk.ai/pac-admission-provenance
@@ -116,6 +147,10 @@ repositories:
variables:
NODE: NC01
LANE: v02
- extends: templates.repositories.agentrunRelease
variables:
NODE: NC01
LANE: release
- extends: templates.repositories.sentinelV03
variables:
NODE: JD01
@@ -279,6 +314,27 @@ consumers:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
fsGroup: 1000
- extends: templates.consumers.agentrunRelease
variables:
NODE: NC01
LANE: release
deliveryProvenance:
required: true
markerValue: admission-pac-v2:agentrun-nc01-release
executionServiceAccountName: agentrun-nc01-release-tekton-runner
gitOps:
repoUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
targetRevision: nc01-release-gitops
sourceArtifact:
mode: embedded-pipeline-spec
renderer: agentrun-control-plane
configRef: config/agentrun.yaml#controlPlane.lanes.nc01-release
pipelineRunPath: .tekton/agentrun-nc01-release.yaml
maxKeepRuns: 8
taskRunTemplate:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
fsGroup: 1000
- extends: templates.consumers.sentinelV03
variables:
NODE: JD01
@@ -619,6 +675,30 @@ templates:
pipeline_run_prefix: "agentrun-${nodeLower}-v02-ci"
service_account: "agentrun-${nodeLower}-v02-tekton-runner"
workspace_pvc_size: 2Gi
agentrunRelease:
id: "agentrun-${nodeLower}-release"
name: "agentrun-${nodeLower}-release"
namespace: agentrun-ci
providerType: gitea
url: https://gitea.pikapython.com/mirrors/pikasTech-agentrun
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
owner: mirrors
repo: pikasTech-agentrun
secretName: "pac-gitea-agentrun-${nodeLower}-release"
tokenKey: token
webhookSecretKey: webhook.secret
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
git_write_url: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
source_branch: release
node: "${NODE}"
gitops_branch: "${nodeLower}-release-gitops"
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/agentrun-release
pipeline_name: "agentrun-${nodeLower}-release-ci-image-publish"
pipeline_run_prefix: "agentrun-${nodeLower}-release-ci"
service_account: "agentrun-${nodeLower}-release-tekton-runner"
workspace_pvc_size: 2Gi
sentinelV03:
id: "sentinel-${nodeLower}-v03"
name: "sentinel-${nodeLower}-v03"
@@ -690,6 +770,17 @@ templates:
argoNamespace: argocd
argoApplication: "agentrun-${nodeLower}-v02"
closeoutGitOpsMirrorFlush: true
agentrunRelease:
id: "agentrun-${nodeLower}-release"
repositoryRef: "agentrun-${nodeLower}-release"
node: "${NODE}"
lane: "${nodeLower}-release"
namespace: agentrun-ci
pipeline: "agentrun-${nodeLower}-release-ci-image-publish"
pipelineRunPrefix: "agentrun-${nodeLower}-release-ci"
argoNamespace: argocd
argoApplication: "agentrun-${nodeLower}-release"
closeoutGitOpsMirrorFlush: true
sentinelV03:
id: "sentinel-${nodeLower}-v03"
repositoryRef: "sentinel-${nodeLower}-v03"
@@ -0,0 +1,46 @@
# AgentRun 开发/生产双环境
范围:AgentRun development`nc01-v02` / `agentrun-v02`)与 production`nc01-release` / `agentrun-release`)双 lane 隔离、自动交付与 HWLAB 绑定。namespace 为隔离边界;不为每个子 issue 新建 MDTODO 文件。
主 issue[UniDesk #2154](https://github.com/pikasTech/unidesk/issues/2154)。
## R1 [completed]
R1 建立 agentrun-release namespace 隔离骨架(#2154),完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R1_Task_Report.md)。
### R1.1 [completed]
创建 GitHub 主 issue 与本 MDTODO 登记,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R1.1_Task_Report.md)。
### R1.2 [completed]
NC01 k3s 创建 agentrun-release Namespace 与 labels,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R1.2_Task_Report.md)。
### R1.3 [completed]
config/agentrun.yaml 声明 nc01-release laneruntime.namespace=agentrun-release),完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R1.3_Task_Report.md)。
### R1.4 [completed]
验证 ns Active、lane 可解析、nc01-v02 plan 无回归,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R1.4_Task_Report.md)。
## R2
R2 agentrun.yaml 模板参数化,nc01-v02 行为不变,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R2_Task_Report.md)。
## R3 [completed]
R3 PaC/Gitea/GitOps 生产链 agentrun-nc01-release,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R3_Task_Report.md)。
## R4 [completed]
R4 DB/Secret/provider 独立物化到 agentrun-release,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R4_Task_Report.md)。
## R5 [completed]
R5 release 命名空间上线 manager 与内网 canary,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R5_Task_Report.md)。
## R6
R6 HWLAB 双环境绑定矩阵(dev→v02prod→release),完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R6_Task_Report.md)。
## R7
R7 文档/CLI help/GC selector 与默认 lane 收敛,完成任务后将详细报告写入[任务报告](./details/agentrun-dev-production-lanes/R7_Task_Report.md)。
@@ -124,6 +124,10 @@
### R5.9 [in_progress]
解决 [AgentRun #356](https://github.com/pikasTech/agentrun/issues/356):修复 runner Job 已不存在且 lease 过期后,非终态 active turn admission 仍以 `session-turn-admission-active` 阻塞同 session `send`;按既有 SPEC 在 memory/PostgreSQL authority 内原子终结或隔离陈旧 admission 并创建新 run/turn/runner,保留 fresh active steer/并发拒绝,不新增第二 session authority、租约、围栏或客户端 retry,并与 PikaOA MVP 并行且不作为业务门禁,完成任务后将详细报告写入[任务报告](./details/agentrun-runtime-reliability/R5.9_Task_Report.md)。
### R5.10 [completed]
解决 [AgentRun #363](https://github.com/pikasTech/agentrun/issues/363):旧 PipelineRun 在新 revision 已发布后才解除 admission 排队并把 `nc01-v0.2-gitops` 回退到旧 sourceCommit,导致 Artificer 重新加载已删除的 `sub2api` AipodSpecPaC promote 必须以 source branch 最新 revision 为 authority,把过期运行处理为 `warning=true``blocking=false`、成功 no-op,禁止覆盖更新 GitOps,并通过自动 PaC 恢复 pika/grok AipodSpec。禁止人工 PipelineRun、Argo sync、数据库修改、第二 authority或 fallback;调试 patch 只允许按 `$unidesk-daddev` P2 最小可逆执行,终态必须由 owning YAML/源码和自动 CI/CD 收敛,完成任务后将详细报告写入[任务报告](./details/agentrun-runtime-reliability/R5.10_Task_Report.md)。
## R6 [completed]
将“先恢复运行面再工程化”固化为 unidesk-subagent 的泛化主线规则:运行面恢复关键路径由主代理控制,低耦合工程化任务及时并行,恢复后继续完成工程化交付;将单 PR 收口、复用 guarded merge 内建 preflight、避免仅补 merge SHA 的重复 PR 和减少碎片化 GitHub 查询固化到 unidesk-gh 及相关 reference,并让 merge 默认摘要直接披露 merge commit 与 mergedAt,完成任务后将详细报告写入[任务报告](./details/agentrun-runtime-reliability/R6_Task_Report.md)。
@@ -0,0 +1,10 @@
# R1.1 任务报告
## 结论
已创建主 issue 与 MDTODO 域文件。
## 证据
- Issue: https://github.com/pikasTech/unidesk/issues/2154
- MDTODO: `docs/MDTODO/agentrun-dev-production-lanes.md`
@@ -0,0 +1,11 @@
# R1.2 任务报告
## 结论
NC01 k3s 已创建 `agentrun-release` Namespacestatus=Active。
## 证据
- labels: `app.kubernetes.io/part-of=agentrun`, `agentrun.pikastech.local/lane=release`, `agentrun.pikastech.local/role=production`, `agentrun.pikastech.local/node=NC01`
- annotations: `unidesk.ai/lane-id=nc01-release`, owner issue #2154
- 命令: `trans NC01:k3s kubectl get ns agentrun-release`
@@ -0,0 +1,21 @@
# R1.3 任务报告
## 结论
`config/agentrun.yaml` 已声明 `controlPlane.lanes.nc01-release`runtime.namespace=`agentrun-release`
## 隔离字段
| 字段 | nc01-v02 | nc01-release |
|------|----------|--------------|
| namespace | agentrun-v02 | agentrun-release |
| source.branch | v0.2 | release |
| workspace | /root/agentrun-v02 | /root/agentrun-release |
| database | agentrun_v02 | agentrun_release |
| kafka client-id | agentrun-v02-manager | agentrun-release-manager |
| secret target ns | agentrun-v02 | agentrun-release |
| maxRunners | 20 | 10 |
## 非本阶段
未部署 release manager、未创建 PaC consumer、未建独立 DB 实例、未切 HWLAB 流量。
@@ -0,0 +1,19 @@
# R1.4 任务报告
## 验证
```bash
bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-v02
# NAMESPACE agentrun-v02 SOURCE v0.2 CHECKS 8
bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-release
# NAMESPACE agentrun-release SOURCE release CHECKS 8
trans NC01:k3s kubectl get ns agentrun-release
# Active
```
## 结论
- release lane 可解析;dev plan 无回归。
- namespace / DB 名 / kafka client-id / secret target ns 均隔离。
@@ -0,0 +1,24 @@
# R1 任务报告
## 目标
建立 AgentRun production 的 **namespace 隔离骨架**#2154 R1)。
## 已完成
1. Issue #2154 + MDTODO `agentrun-dev-production-lanes`
2. NC01 创建 `agentrun-release` Namespace
3. YAML 声明 `nc01-release` lane(与 `nc01-v02` 并列)
4. plan 双 lane 可跑;dev 无回归
## 未做(后续 R2R7
模板参数化、PaC/GitOps、独立 DB 物化、manager 上线、HWLAB 绑定、文档全量收敛。
## 关键命令
```bash
trans NC01:k3s kubectl get ns agentrun-release
bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-release
bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-v02
```
@@ -0,0 +1,12 @@
# R3 任务报告
## 完成
- `config/platform-infra/pipelines-as-code.yaml``agentrunRelease` repository/consumer 模板 + NC01 实例 `agentrun-nc01-release`
- `config/platform-infra/gitea.yaml`mirror key `agentrun-nc01-release`branch `release`gitops `nc01-release-gitops`
- control-plane 对象已 applyTekton SA/Role/Pipeline、Argo AppProjectApplication 因 gitops 分支未解析保持 ComparisonError,已去掉 automated sync 防误伤 bootstrap
## 未完成
- agentrun 仓创建 `release` / `nc01-release-gitops``.tekton/agentrun-nc01-release.yaml`
- PaC Repository CR / webhook 在集群侧 bootstrapconsumer 权威已可解析)
@@ -0,0 +1,8 @@
# R4 任务报告
## 完成
- host PostgreSQL 创建 role/database `agentrun_release`
- owner secret`agentrun/nc01-release-mgr-db.env`(不入库)
- `secret-sync --lane nc01-release --confirm`16 个 secret 就绪(2 个 omitted 非阻塞)
- 独立 target namespace`agentrun-release`
@@ -0,0 +1,14 @@
# R5 任务报告
## 完成
- 渲染 `runtime-release` mgr/runner-rbac 并 server-side apply
- 镜像复用现网 env digest `sha256:e080ba9a…`(与 v02 同构建物,namespace/DB 隔离)
- Deployment `agentrun-mgr` Ready 1/1
- `/health/live``/health/readiness``live=true ready=true`DB migration `013_v02_runner_dispatch_outcome` ready
- 内网 Service`agentrun-mgr.agentrun-release.svc.cluster.local:8080`
- v02 manager 仍 Ready 1/1
## 说明
bootstrap 字段 manager`unidesk-agentrun-release-bootstrap`;后续以 GitOps 为准时需在 agentrun 仓写入 runtime-release 并由 PaC 推进。
@@ -0,0 +1,50 @@
# R5.10 P0 事故复盘
## 结论
本次事故不是生产 lane writer 修改开发环境,也不是数据库迁移依赖。真正根因是一个此前被 admission 阻塞的旧 AgentRun PaC PipelineRun,在较新 `b258520` 已成功发布后才开始执行,并以较新的 GitOps commit 为父提交,把 `sourceCommit` 逆序写回旧值 `0ca2c73`。Argo 正常同步了错误 desiredmanager 因此重新加载旧 `sub2api` Artificer AipodSpec,新任务在 admission 阶段失败。
## 影响
- NC01/nc01-v02 manager desired 与源码 `v0.2` 分叉。
- Artificer 从 `gpt-pika/grok` 回退到已删除的 `sub2api` provider。
- HWLAB 公网入口修复无法派单,主线被 CI/CD 运行面故障阻塞。
- 两个重复的生产 lane writer 被停止;后续证据确认它们不是 `bc12e93` 的作者。
## 时间线
- 较新 PipelineRun `agentrun-nc01-v02-ci-b258520...` 成功,生成 GitOps `dba70ac`
- 旧 `agentrun-nc01-v02-ci-0ca2...` 在 admission 解除后延迟启动,生成父提交为 `dba70ac``bc12e93`,但把 sourceCommit 写回 `0ca2c73`
- PR [UniDesk #2157](https://github.com/pikasTech/unidesk/pull/2157) 与 [AgentRun #364](https://github.com/pikasTech/agentrun/pull/364) 加入 stale/divergent/unverifiable revision 成功 no-op 逻辑。
- #364 的自动 PipelineRun 在 `place-scripts` init 阶段失败;共享 schema validator 与 Ajv 被明文嵌入,单步脚本约 172 KiBinit container 退出 255。
- PR [UniDesk #2160](https://github.com/pikasTech/unidesk/pull/2160) 将内嵌载荷改为 renderer 侧 gzip/base64,脚本降至 79,395 bytes[AgentRun #365](https://github.com/pikasTech/agentrun/pull/365) 更新 repo-owned source artifact。
- #365 merge 自动产生 PipelineRun `agentrun-nc01-v02-ci-5d7ede...`,成功生成 GitOps `56b5f3ed...` 并滚动 manager。
## 根因与促成因素
- promotion 缺少 incoming revision 相对 source branch 与当前 GitOps desired 的 ancestry/新旧判定,旧任务可以覆盖新 desired。
- admission 阻塞把旧任务延迟到新任务之后执行,暴露了 PipelineRun 完成顺序不等于 source revision 顺序。
- schema warning-only 实现把完整依赖明文嵌入 Tekton step,缺少对 place-scripts 载荷尺寸的运行面约束验证。
- PaC `status/history/debug-step` 间歇性空 stdout,增加了定位成本;该可见性缺口由 [UniDesk #2165](https://github.com/pikasTech/unidesk/issues/2165) 独立跟踪,不改变交付 authority。
## 修复
- incoming revision 已过期、分叉或相对当前 desired 无法验证时,PipelineRun 保持成功终态、输出 `blocking=false` warning,并保留现有 GitOps commit。
- ancestry 判断只使用 source clone 已有 refs 与当前 GitOps `source.json`,不增加第二 authority 或额外网络 fetch。
- validator/Ajv 由 renderer 预压缩,运行时解压到有界临时文件;schema 与 OTel 失败仍只产生 warning,业务发布继续。
- 全部恢复只由正常 PR merge 的 GitHub webhook -> Gitea -> PaC -> Tekton -> GitOps/Argo 自动链完成;未执行人工 PipelineRun、mirror sync、Argo sync、runtime patch 或数据库操作。
## 验证
- schema stage 定向 Bun 测试:5 pass0 fail;未运行 Vitest。
- AgentRun source artifactaligned=true、provenance=true;生成 YAML 可解析,`gitops-promote` 脚本 `sh -n` 通过。
- 自动 PipelineRun `agentrun-nc01-v02-ci-5d7ede...`Succeeded。
- GitOps commit`56b5f3ed00f28b04e47f2e9baf3792d745d2a375`
- manager Deployment `AGENTRUN_SOURCE_COMMIT=5d7ede07543d6f8d9924ce9853420a4c6051eabd`,新 Pod ready。
- `agentrun describe aipodspec/Artificer`lane=`v0.2`、profile=`gpt-pika`、provider=`NC01`、model=`gpt-5.6-sol`provider SecretRefs 为 `gpt-pika``grok`,旧 `sub2api` 已消失。
## 长期判定
- PipelineRun 的 source revision 决定新旧,排队与完成时间不能成为 desired authority。
- runtime patch 可按 `$unidesk-daddev` P2 用于调试或临时恢复,但不能冒充终态;本事故未使用 patch,终态已由正常自动链收敛。
- CI/CD 校验继续 warning-only,不得因 schema/OTel 漂移关闭功能或阻塞滚动。
@@ -0,0 +1,3 @@
# R1.5.9.13.1 任务报告
PaC admission provenance 漂移已按用户要求降级为非阻塞 warning。共享 Repository 与独立 consumer 可以完成 bootstrap,正常产品 PR merge 产生的 PipelineRun 不再被旧一致性门禁阻塞。
@@ -0,0 +1,3 @@
# R1.5.9.13 任务报告
已建立仅跟随产品 master 的 pikaoa-test-nc01 测试 CI/CD。正常产品 PR merge 自动触发测试 PipelineRun,测试 namespace 保持为 pikaoa-test,未触发生产 release consumer。测试 Web、API、Worker、数据库和完整合同/发票主路径均已验收。
@@ -0,0 +1,3 @@
# R1.5.9 任务报告
现有 NC01 k3s 已建立固定 pikaoa-test namespace,使用 PK01 host PostgreSQL 独立测试库与角色,并以 YAML 声明 NodePort 32080。CLI 单步部署、测试 CI/CD、桌面与移动 Web、合同多版本、发票关联及废弃、OTel 和 Prometheus 均已通过真实验收。
@@ -0,0 +1,3 @@
# R1.6 任务报告
双仓生产交付改动已审核合并。产品 release 的正常 PR merge 自动触发生产 PipelineRunAPI、Worker、Web 镜像构建与 GitOps 发布全部成功;未人工创建 PipelineRun。
@@ -0,0 +1,3 @@
# R1.7 任务报告
生产原入口 https://oa.pikapython.com 已完成验收:TLS 与首页返回 200,管理员公网登录成功;员工、模块、伙伴、合同、发票与审计接口可用。公网写入验收完成甲方分类、伙伴、合同 v1/v2、关联发票与废弃,全部资源 ID 唯一且版本关系正确。桌面与 390x844 移动视口登录工作台通过且无页面或控制台错误。NC01 API、Web、Worker、FRPC 均 Ready 1/1Argo 为 Synced/HealthyPrometheus 生产 API/Worker 数据库就绪值均为 1,登录 trace 共 18 spans、0 error。
@@ -0,0 +1,3 @@
# R1.8.1 任务报告
已纠正 PikaOA 长期参考中“生产尚未 bootstrap”的过时事实,补充 release source authority、生产 namespace、PK01 数据库、fresh initializer、公网入口及非阻塞门禁边界。新增运行面、Argo、公网、Prometheus、桌面/移动 Web 与 OTel 的最短只读验收路径,使后续同类核查无需重新搜索生产状态和入口。
@@ -0,0 +1,3 @@
# R1.8 任务报告
PikaOA MVP issue、MDTODO 与生产验收报告已收口,测试与生产交付 issue 已关闭,主 issue 已追加上线证据。post-task 完成两项通用改进:PR #2161 固化生产最短只读验收路径;agent_skills PR #14 新增 Playwright fill-file 安全 Secret 填写入口并通过不泄露 smoke。已删除所有 clean 且被 master/release 吸收的 PikaOA worktree 和本地分支;未吸收的附件备份 worktree及存在用户修改的 Web worktree按保护规则保留。
@@ -78,3 +78,28 @@
- 无源码或定向验证阻塞。
- 运行面验收仍等待 PR 合并及主代理执行受控 bootstrap,因此任务保持 `in_progress`
## 启动 backlog 后续
- warning-only admission 收敛后,新 PaC run 已可创建,但 NC01 官方 watcher 在重放历史 PipelineRun 时暴露第二断点:
- Pod `pipelines-as-code-watcher-7f6c87497c-7lhfh``/live` 在集中 reconcile 期间超过 `5s` timeout
- 默认 liveness 在 `initialDelaySeconds: 5``failureThreshold: 3` 下连续失败并重启容器;
- 同期 readiness 失败只影响流量就绪,liveness 重启会反复打断历史 backlog 初始同步。
- owning YAML 新增 `release.workloads.watcher`,明确声明:
- watcher Deployment 与 container identity
- startup、readiness 与 liveness 三类 HTTP probe
- startup backlog 窗口和 liveness 延迟全部由 YAML 持有,代码不提供隐藏数值默认值。
- PaC bootstrap 下载官方 v0.48.0 release manifest 后,在 apply 前按 YAML 精确匹配 watcher Deployment/container 并渲染探针:
- 未匹配 Deployment 或 container 时直接失败;
- 不使用 runtime patch、第二份 manifest 或手工 `kubectl patch`
- 后续官方 release reapply 仍由同一 bootstrap 渲染相同 desired state。
- 启动窗口采用:
- startup:延迟 `30s`,每 `10s` 检查,单次 timeout `5s`,允许 `60` 次失败;
- liveness:延迟 `180s`,每 `10s` 检查,允许 `6` 次失败;
- readiness:保留 `5s` 延迟、`10s` 周期和 `3` 次失败,只控制就绪状态。
- 定向验证:
- `bun test scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts``13 pass, 0 fail, 66 expect()`
- 使用真实 v0.48.0 上游 manifest 验证:共解析 `35` 个对象,精确命中 `pipelines-as-code-watcher/pac-watcher` 并渲染三类 YAML-owned probe
- `bun scripts/cli.ts platform-infra pipelines-as-code plan --target NC01 --consumer hwlab-nc01-v03 --json`:成功披露 watcher probes`warnings=[]`
- `git diff --check`:通过。
- 合并后只允许由 PaC migrated consumer 的正常 PR merge 自动链触发交付;不得人工执行 PipelineRun、mirror、trigger、sync、flush、Argo sync、runtime patch 或删除历史业务对象。
@@ -0,0 +1,46 @@
# R1 任务报告
## 关联
- 来源任务:[UniDesk #2133](https://github.com/pikasTech/unidesk/issues/2133)MDTODO `R9.1`
- 改进 issue[UniDesk #2153](https://github.com/pikasTech/unidesk/issues/2153)。
- 后续语音主线:[UniDesk #2148](https://github.com/pikasTech/unidesk/issues/2148),本改进不构成其门禁。
## 问题判断
R9.1 的远程 Docker GPU 实验需要多次手写后台 PID、status、log、证据和清理命令。仓库已有 60 秒短连接规则及专用 async 实现,但 `unidesk-trans` 没有面向一次性远程实验的参数化模板。
已查重:
- `#431/#435` 只处理 Playwright 专用 submit
- `#2148` 已吸收稳定 Torch/TorchAudio ABI 预检经验,但不是工具或 skill 改进;
- 未发现覆盖通用临时 build、Docker GPU、模型下载和硬件 smoke 的重复 issue 或 MDTODO。
## 交付
- 新增 `.agents/skills/unidesk-trans/references/remote-experiment-smoke.md`
- 定义 `route``work_dir``resource_prefix``job_id``command`、artifact allowlist 和停止条件;
- 提供一次 preflight
- 提供 job 脚本、submit 和 bounded poll
- 统一原入口 evidence 字段;
- 提供按 label 或资源前缀执行的幂等 cleanup;
- 明确网络、registry、ABI、OOM 和模型失败必须分层记录。
- 更新 `unidesk-trans` skill 与 operation reference 索引。
- 新增独立 MDTODO,避免把改进夹入 SelfMedia 业务任务。
## 验证
- 五个 Bash 代码块替换占位符后均通过 `sh -n`
- Markdown fence、reference 文件和 `git diff --check` 通过。
- NC01 原入口完成一个真实最小 smoke:
- submit 返回 PID
- poll 返回 `state=finished exit=0`
- artifact 内容为 `template-smoke-ok`
- stdout/stderr 均为 `0` bytes
- cleanup 复查为 `clean`
## 边界
- 未新增 CLI 子命令、后台服务、安全机制或正式运行面。
- 未硬编码 Qwen、CosyVoice、D518、镜像、模型或端口。
- 未修改 `#2148`,未触发 Docker、GPU、CI/CD 或 rollout。
+10 -6
View File
@@ -89,7 +89,7 @@
#### R1.5.8 [completed]
修复 `pikaoa test-target` 临时运行面缺失附件配置与数据库迁移启动链,使 fixture 能渲染当前产品可启动的 namespace-local 后端运行面,保持未声明 target 与 `validationOnly` 全程无远端变更,执行记录见 [pikasTech/unidesk#2053](https://github.com/pikasTech/unidesk/issues/2053),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.8_Task_Report.md)。
#### R1.5.9 [in_progress]
#### R1.5.9 [completed]
按用户确认方案在现有 NC01 k3s 的独立 pikaoa-test namespace 建立 YAML-first 测试运行面,使用 PK01 host PostgreSQL 独立测试 database/role 和 YAML hostIP 端口;先通过受控 CLI 单步调通,再接入跟随产品 master 的独立测试 CI/CD,执行记录见 [pikasTech/unidesk#2058](https://github.com/pikasTech/unidesk/issues/2058),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.9_Task_Report.md)。
@@ -129,13 +129,13 @@
##### R1.5.9.12 [completed]
修复 PikaOA HTTP hostIP 测试入口因 crypto.randomUUID 不可用导致 Web 登录失败的问题,并完成桌面与移动端真实入口验收(pikainc/pikaoa#22),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.9.12_Task_Report.md)。
##### R1.5.9.13 [in_progress]
##### R1.5.9.13 [completed]
接入仅跟随产品 master 的 pikaoa-test-nc01 独立测试 CI/CD,通过正常 source PR merge 自动交付 NC01 pikaoa-test,保持生产 consumer 不触发(pikasTech/unidesk#2105),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.9.13_Task_Report.md)。
###### R1.5.9.13.1 [in_progress]
###### R1.5.9.13.1 [completed]
修复 PikaOA 首次 PaC bootstrap 未同步 admission provenance desired policy/binding 的缺口,使配置指纹漂移降级为非阻塞 warning 且正常产品 PR merge 产生的 PipelineRun 能继续执行,执行记录见 [pikasTech/unidesk#2127](https://github.com/pikasTech/unidesk/issues/2127),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.9.13.1_Task_Report.md)。
### R1.6
### R1.6 [completed]
审核并合并双仓 PR,执行首次受控 PaC bootstrap,由正常 source PR merge 自动发布,依赖 R1.3-R1.5,完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.6_Task_Report.md)。
@@ -151,10 +151,14 @@
##### R1.6.1.3 [completed]
建立仅跟随产品 release 的 PikaOA 生产 PaC/Tekton/GitOps/Argo lane,以 fresh initializer 在 NC01 pikaoa namespace 启动 API/Worker/Web/附件 PVC,接入 PK01 host PostgreSQL、OTel/Prometheus 和 oa.pikapython.com TLS 公网入口,执行记录见 [pikasTech/unidesk#2129](https://github.com/pikasTech/unidesk/issues/2129),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.6.1.3_Task_Report.md)。
### R1.7
### R1.7 [completed]
通过 CLI、OTel、Prometheus 和 https://oa.pikapython.com 桌面/移动原入口验证完整主路径,依赖 R1.6,完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.7_Task_Report.md)。
### R1.8
### R1.8 [completed]
完成 issue、MDTODO、阶段报告、post-task、已吸收 worktree 与分支清理,依赖 R1.7,完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.8_Task_Report.md)。
#### R1.8.1 [completed]
更新 PikaOA 生产上线后的长期参考与最短只读验收路径,纠正“生产尚未 bootstrap”的过时事实并关联 [pikasTech/unidesk#2159](https://github.com/pikasTech/unidesk/issues/2159),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.8.1_Task_Report.md)。
@@ -0,0 +1,9 @@
## R1 [completed]
依据 [UniDesk #2153](https://github.com/pikasTech/unidesk/issues/2153) 为 `unidesk-trans` 增加参数化远程临时实验 smoke 模板:
- 统一一次 preflight、后台 job submit/poll、原入口 evidence、停止条件和幂等 cleanup
- 不得硬编码节点、模型、镜像或端口;
- 不新增 CLI 子命令;
- 不阻塞 SelfMedia R9.1.1
- 完成任务后将详细报告写入[任务报告](./details/trans-remote-experiment-smoke/R1_Task_Report.md)。
+2 -2
View File
@@ -55,7 +55,7 @@
- 分流顺序:
- 可复用工作流进入 skill `SKILL.md`,例如:
- `$dad-dev``$unidesk-cicd``$unidesk-gh`
- `$unidesk-daddev``$unidesk-cicd``$unidesk-gh`
- `$unidesk-trans``$unidesk-otel``$unidesk-webdev`
- `$unidesk-ymalops`
- 稳定项目约束、工作区规则、架构边界和验收标准进入 `docs/reference/*.md`
@@ -132,7 +132,7 @@
- CLI 行为与输出:`docs/reference/cli.md`
- YAML-first 配置:`docs/reference/yaml-first-ops.md``$unidesk-ymalops`
- 平台基础设施:`docs/reference/platform-infra.md`;涉及 Sub2API 时使用 `$unidesk-sub2api`
- 分布式现场修复:`$dad-dev``docs/reference/devops-hygiene.md`
- 分布式现场修复:`$unidesk-daddev``docs/reference/devops-hygiene.md`
- CI/CD 与发布:`$unidesk-cicd``docs/reference/cli.md`
- GitHub issue 与 PR 写入:`$unidesk-gh`
- Trans/远端补丁传输:`$unidesk-trans``docs/reference/cli.md`
+19 -4
View File
@@ -46,26 +46,41 @@ AgentRun 源码仓目标 branch 的任何变更都必须通过 GitHub PR 合并
## 部署目标
AgentRun 废弃旧 `dev/prod` 和非 NC01 node 运行口径。固定部署目标是 NC01 k3s 的 AgentRun namespace
AgentRun 固定在 NC01 k3s,按 **namespace 隔离** 拆分 development / production 两条 lane(同集群,非独立物理集群)。身份真相是 `config/agentrun.yaml#controlPlane.lanes`;跟踪见 [UniDesk #2154](https://github.com/pikasTech/unidesk/issues/2154)。
| 角色 | Lane | Namespace | Source branch | Workspace | 默认 |
|------|------|-----------|---------------|-----------|------|
| Development | `nc01-v02` | `agentrun-v02` | `v0.2` | `/root/agentrun-v02` | 是(`controlPlane.default.lane` |
| Production | `nc01-release` | `agentrun-release` | `release` | `/root/agentrun-release` | 否,必须显式 `--lane nc01-release` |
```text
NC01:k3s namespace agentrun-v02
NC01:k3s namespace agentrun-v02 # development runtime
NC01:k3s namespace agentrun-release # production runtimemanager 已上线;内网 ClusterIP
```
- production 内网入口:`http://agentrun-mgr.agentrun-release.svc.cluster.local:8080`
- production DBhost PostgreSQL 独立库 `agentrun_release`(与 `agentrun_v02` 隔离)
- production PaC consumer`agentrun-nc01-release`YAML 已声明;`nc01-release-gitops` 分支与自动链待 agentrun 仓补齐)
- 当前 production manager 可由 YAML 渲染 manifest 做 bootstrap 上线;完整 PR merge → GitOps 自动链仍属 #2154 后续收敛
所有 k3s 操作必须使用 UniDesk route 语法:
```bash
trans NC01:k3s kubectl get pods -n agentrun-v02
trans NC01:k3s kubectl get ns agentrun-release
bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-v02
bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-release
```
不得把临时 NodePort、host port、pod IP、provider-gateway 业务 HTTP proxy 或一次性 port-forward 固化为 AgentRun 部署路径。任何公网入口、UniDesk/HWLAB 集成入口或跨服务访问路径,都必须先通过 AgentRun 仓库内经过审查的变更引入;UniDesk 只在后续记录对应运维入口。
## 受控 CI/CD 入口
AgentRun 目标必须从 `config/agentrun.yaml` 解析,不得从 service repo 的 `deploy.json` 或 URL 片段推断。Delivery authority 还必须与 `config/platform-infra/gitea.yaml``config/platform-infra/pipelines-as-code.yaml` 的 consumer/repository 组合校验。当前默认 `NC01/nc01-v02` 是 PaC migrated consumer;目标 source branch 的 GitHub PR merge 是唯一正式交付触发。
AgentRun 目标必须从 `config/agentrun.yaml` 解析,不得从 service repo 的 `deploy.json` 或 URL 片段推断。Delivery authority 还必须与 `config/platform-infra/gitea.yaml``config/platform-infra/pipelines-as-code.yaml` 的 consumer/repository 组合校验。当前默认 `NC01/nc01-v02` 是 PaC migrated consumer;目标 source branch 的 GitHub PR merge 是唯一正式交付触发。`nc01-release` 的独立 PaC consumer 属于 #2154 后续阶段,R1 仅完成 namespace 与 lane 声明。
```bash
bun scripts/cli.ts agentrun control-plane status
bun scripts/cli.ts agentrun control-plane status --node NC01 --lane nc01-v02
bun scripts/cli.ts agentrun control-plane status --node NC01 --lane nc01-release
bun scripts/cli.ts platform-infra pipelines-as-code status --target NC01 --consumer agentrun-nc01-v02
bun scripts/cli.ts platform-infra pipelines-as-code history --target NC01 --consumer agentrun-nc01-v02 --limit 10
bun scripts/cli.ts agentrun control-plane cleanup-runners --node NC01 --lane nc01-v02 --dry-run
+25 -3
View File
@@ -60,9 +60,31 @@ If a manual repair is needed to unblock the platform, the durable fix must be co
## 分布式敏捷流程
“分布式敏捷”是 UniDesk 对 distributed agile field repair 的固定流程名;通用 P1/P2/P3/P4 阶段、禁止行为和证据边界由 `$dad-dev` skill 维护,本参考不再重复展开。UniDesk 项目内只保留下面的特有约束:必须使用结构化 `trans`/UniDesk CLI 进入真实 provider、pod、host bridge 或 service port;运行面热补只能证明方向或临时恢复,不能成为隐藏部署真相;持久化完成必须回到 Git/PR/CI/CD 后原入口复测。
“分布式敏捷”是 UniDesk 对 distributed agile field repair 的固定流程名;通用 P1/P2/P3/P4 阶段、禁止行为和证据边界由 `$unidesk-daddev` skill 维护,本参考不再重复展开。UniDesk 项目内只保留下面的特有约束:
固定主 repo 是 source truth anchor,不是源码/运行面 scratch 区。会产生源码、配置、issue closeout、部署脚本、验收产物或高风险 dad-dev / post-task 交付的工作,执行前必须先从目标 fixed repo 的最新 remote/base 创建任务专属 `.worktree/<task>`,后续编辑、验证、提交、push 和受控 CLI 写操作都在该 worktree 内完成。UniDesk 任务 worktree 必须通过 `bun scripts/cli.ts dev-env worktree add .worktree/<task> --branch <branch> [--from origin/master]` 创建,让 `.worktreecopy` 白名单内的本地配置自动复制到新 worktreefixed repo 只用于 `git fetch``git status`、快进同步、读取规则和这个受控创建入口。其中已有的并行未提交修改默认保持不动,不纳入当前任务,也不要用 reset、checkout 或删除来“清理”。
- 必须使用结构化 `trans`/UniDesk CLI 进入真实 provider、pod、host bridge 或 service port
- P2 允许按调试需要实施最小、可逆的运行面 patch 或热补,
但它只能证明方向或临时恢复,不能成为隐藏部署真相、第二 authority、
GitOps desired、自动交付成功或最终验收证据;
- P3 必须把有效变化写回 owning YAML、源码或 renderer
经正常 PR 与自动 CI/CD/GitOps 交付;临时 patch 必须撤销,
或由已确认的声明式交付覆盖;
- P4 使用用户原入口复测,不能用单次手工成功替代终态。
固定主 repo 是 source truth anchor,不是源码/运行面 scratch 区。
- 会产生源码、配置、issue closeout、部署脚本、验收产物,
或高风险 unidesk-daddev / post-task 交付的工作,
执行前必须从目标 fixed repo 的最新 remote/base
创建任务专属 `.worktree/<task>`
- 后续编辑、验证、提交、push 和受控 CLI 写操作都在该 worktree 内完成;
- UniDesk 任务 worktree 必须通过
`bun scripts/cli.ts dev-env worktree add .worktree/<task> --branch <branch> [--from origin/master]`
创建,让 `.worktreecopy` 白名单内的本地配置自动复制到新 worktree;
- fixed repo 只用于 `git fetch``git status`、快进同步、读取规则
和这个受控创建入口;
- 其中已有的并行未提交修改默认保持不动,不纳入当前任务;
- 禁止用 reset、checkout 或删除来“清理”。
The root UniDesk checkout at `/root/unidesk` is the fixed main worktree and must stay on `master`. Non-`master` work belongs in a task-specific `.worktree/<task>`. Unexpected local changes are presumed to belong to another concurrent task: preserve them, do not reset, checkout, delete or rewrite them, and scope the current task's commit to its own files.
@@ -90,7 +112,7 @@ CONSTAR、控之星、71-FILTER、71-FREQ、PLC 项目和 PLC 控制器任务是
When UniDesk's own CLI, wrapper or controlled toolchain is repaired, merge the durable fix into `master` before relying on it for the original operation. The checkout that actually executes the tool must then contain that merged revision. If a fixed checkout cannot fast-forward because of unrelated changes, preserve it and run the tool from a clean worktree based on current `origin/master`; never reset or overwrite concurrent work to update the executable path.
在模型 provider、API provider、硬件链路、跨平台 bridge、CLI/trans/tran 或高频工具链问题上,判定外部 blocker 前仍需完成 UniDesk 的防误判核查:确认当前 runtime config / Secret key presence / env / proxy / NO_PROXY / endpoint / args,使用实际目标运行面复现,并尽量与 UniDesk/HWLAB 成熟实现对照。用户反馈或新证据推翻 blocker 判断时,立即切回 `$dad-dev` 的现场修复闭环。
在模型 provider、API provider、硬件链路、跨平台 bridge、CLI/trans/tran 或高频工具链问题上,判定外部 blocker 前仍需完成 UniDesk 的防误判核查:确认当前 runtime config / Secret key presence / env / proxy / NO_PROXY / endpoint / args,使用实际目标运行面复现,并尽量与 UniDesk/HWLAB 成熟实现对照。用户反馈或新证据推翻 blocker 判断时,立即切回 `$unidesk-daddev` 的现场修复闭环。
如果某个现场步骤因为 quoting、route 定位、kubeconfig、输出体积或缺少 helper 而反复痛苦,优先改进 UniDesk passthrough / CLI 并在本文件的 `Distributed Command Passthrough``docs/reference/cli.md` 中记录稳定入口,不要沉淀一批一次性 shell 菜谱。
+2 -2
View File
@@ -35,7 +35,7 @@ The shared Prometheus stack may discover application monitors across namespaces
## GitOps And Control Plane
Monitoring infrastructure must be declared as Git-backed desired state and applied through a controlled UniDesk or G14 GitOps path. A temporary `kubectl apply` may be used only as a `$dad-dev` P2 experiment; it must be followed by a durable source change and GitOps/CLI validation.
Monitoring infrastructure must be declared as Git-backed desired state and applied through a controlled UniDesk or G14 GitOps path. A temporary `kubectl apply` may be used only as a `$unidesk-daddev` P2 experiment; it must be followed by a durable source change and GitOps/CLI validation.
Current durable control surface:
@@ -92,7 +92,7 @@ Durable validation after rollout must prove:
- No public HWLAB or UniDesk FRP endpoint exposes raw Prometheus, Grafana or application `/metrics`.
- CLI or controlled proxy output can answer a bounded query and reports the target namespace/lane/source of the data.
Metrics are supporting observability evidence. They do not replace `$dad-dev` source-level tests, CI/CD provenance or original-entry validation for HWLAB issues.
Metrics are supporting observability evidence. They do not replace `$unidesk-daddev` source-level tests, CI/CD provenance or original-entry validation for HWLAB issues.
## Application Closeout Contract
+1 -1
View File
@@ -6,7 +6,7 @@
- HWLAB 固定运行面、工作区、原入口验收与凭据边界:`docs/reference/hwlab.md`
- Gitea source authority、PaC/Tekton、GitOps/Argo 与自动交付边界:`docs/reference/platform-infra.md#gitea-与-pipelines-as-code-边界`
- 跨节点现场调查、持久化和原入口复测:`$dad-dev`
- 跨节点现场调查、持久化和原入口复测:`$unidesk-daddev`
- CI/CD 只读观察、scoped help 与 legacy 判定:`$unidesk-cicd` 及其按需 reference。
- 固定开发机、Master 构建限制和任务 worktree`docs/reference/dev-environment.md`
+14 -1
View File
@@ -7,7 +7,20 @@
- UniDesk 指挥侧 workspace`/root/unidesk`,固定使用 `master`,开始前执行 `git status``git pull --ff-only origin master`
- HWLAB 开发、部署和验证固定只使用 NC01。`config/hwlab-node-lanes.yaml` 是 node、lane、workspace、CI/CD repo、namespace、GitOps path、公网入口和 Secret sourceRef 的配置真相,当前默认目标必须是 `NC01/v03`
- HWLAB 固定主 workspace 是 `NC01:/root/hwlab-v03`,固定跟踪 `v0.3`。源码修改、PR 准备、repo 内验证、GitOps render 和 rollout 修复都必须在该固定 workspace 下创建任务级 `.worktree/<task>`master server 本地 `/root/HWLAB`、一次性 clone、runner clone、pod 内副本或非 NC01 workspace 只能做只读对照,不能承担开发 source truth。
- HWLAB v0.3 delivery authority`config/hwlab-node-lanes.yaml``config/platform-infra/gitea.yaml``config/platform-infra/pipelines-as-code.yaml` 组合确认。NC01/v03 精确解析为 PaC consumer 后,目标 branch 的 GitHub PR merge 是唯一交付触发;完整自动链为 GitHub webhook -> Gitea controlled mirror -> immutable snapshot -> Gitea webhook -> PaC -> Tekton -> GitOps/Argo -> runtime。默认 help、status 与 Next 只给只读 status/history;自动链故障必须修 owning YAML、controller 或源码,不得用 trigger、refresh、mirror sync/flush、人工 PipelineRun 或直接 Gitea push 补齐。
- HWLAB v0.3 delivery authority
- 由 `config/hwlab-node-lanes.yaml``config/platform-infra/gitea.yaml`
`config/platform-infra/pipelines-as-code.yaml` 组合确认;
- NC01/v03 精确解析为 PaC consumer 后,
目标 branch 的 GitHub PR merge 是唯一交付触发;
- 完整自动链为 GitHub webhook -> Gitea controlled mirror -> immutable snapshot
-> Gitea webhook -> PaC -> Tekton -> GitOps/Argo -> runtime
- 默认 help、status 与 Next 只给只读 status/history
- 自动链故障必须修 owning YAML、controller 或源码,
不得用 trigger、refresh、mirror sync/flush、人工 PipelineRun 或直接 Gitea push 补齐;
- 调试或临时恢复确需运行面 patch 时走 `$unidesk-daddev` P2
- patch 不得改变交付 authority 或冒充终态;
- 结论必须写回 owning YAML/源码并由正常 PR 自动交付;
- 临时 patch 随后撤销或由声明式交付覆盖。
- 进入任何 HWLAB 工作前,按 `NC01/v03` 解析 route/workspace/sourceBranch/kubeRoute/runtime namespace 做预检、快进和验证:工作面是 `NC01:/root/hwlab-v03`、source branch 是 `v0.3`、k3s route 是 `NC01:k3s`、runtime namespace 是 `hwlab-v03`、公网入口由 YAML 的 NC01 target 声明。
- NC01 的 node-local registry 是 k3s workload/PVC,不是 host Docker registry。`hwlab nodes control-plane infra status --node NC01 --lane v03` 应显示 registry workload ready、PVC bound 和 endpoint ready;必须通过受控 `runtime-image preload``infra tools-image build/status` 补齐 BuildKit、runtime/base 和 tools image,再把 HWLAB/AgentRun status 与 web-probe smoke 作为可用性证据。
- HWLAB 项目内长期规则入口仍以目标 repo 的 `AGENTS.md` 为准。进入已解析的目标 workspace 后,必须重新读取该 workspace 的规则文件;不能只凭主 server 的压缩上下文继续操作。
+51 -1
View File
@@ -11,6 +11,16 @@
- 测试 namespace 不根据 commit、instance 或任务动态生成。
- `pikaoa``pikaoa-ci` 是正式交付保护 namespace。
- 测试入口不得渲染、覆盖或删除保护 namespace 中的对象。
- 生产运行面固定使用:
- source branch`release`
- consumer`pikaoa-nc01`
- runtime namespace`pikaoa`
- CI namespace`pikaoa-ci`
- Argo Application`pikaoa-nc01`
- 公网入口:`https://oa.pikapython.com`
- 生产交付与公网配置分别以以下 selector 为真相:
- `config/pikaoa.yaml#releaseRuntime.targets.NC01`
- `config/pikaoa.yaml#delivery.targets.NC01`
## 数据库边界
@@ -122,9 +132,49 @@
- 不创建 namespace-local PostgreSQL
- 不把测试 CI/CD 变成生产交付或用户业务的阻塞门禁。
- GitOps 接管后,手动 `test-target start|stop` mutation 仅用于暂停 Argo 自动同步后的有界调试;禁止 CLI direct-apply 与 Argo 同时写入。
- 生产 `pikaoa-nc01` 保留 `release` source authority,但当前不 bootstrap、不生成 `.tekton`、不创建 PipelineRun。
- 生产 `pikaoa-nc01` 已完成 bootstrap
- 产品 `release` 只生成 `.tekton/pikaoa-nc01-pac.yaml`
- 正常 `release` PR merge 是生产 PipelineRun 的唯一触发入口;
- API、Worker、Web 并行构建并以 digest 发布到生产 GitOps branch
- Argo Application `pikaoa-nc01` 自动同步 `pikaoa` namespace
- 禁止人工补建 PipelineRun 代替 source merge。
- 生产使用 PK01 host PostgreSQL 独立 `pikaoa` database/role,不使用 namespace-local PostgreSQL。
- 生产初始化模式是 `fresh-database-only`,不实现旧数据迁移、兼容或回滚。
- 版本、审计和配置一致性异常只产生 `blocking=false` warning,不作为 MVP 业务门禁。
- PR、构建或单测不能替代固定 NodePort 原入口验收。
## 生产最短只读验收
- 先执行运行面与 Argo 对账:
```bash
trans NC01:k3s kubectl get deploy,pod,svc,pvc -n pikaoa -o wide
trans NC01:k3s kubectl get application -n argocd pikaoa-nc01
```
- 运行面通过判定:
- API、Web、Worker、FRPC Deployment 全部 Ready
- Pod 全部 Running 且没有新增重启;
- 附件 PVC 为 Bound
- Argo 为 `Synced``Healthy`
- 再验证公网和共享可观测性:
```bash
curl -fsSI https://oa.pikapython.com/
bun scripts/cli.ts platform-infra observability metrics-query \
--target NC01 \
--query 'pikaoa_database_ready' \
--full
```
- 公网与可观测性通过判定:
- 公网 HTTPS 返回 200
- 生产 API 和 Worker 的 `pikaoa_database_ready` 均为 1
- 使用 `$unidesk-webdev` 完成管理员桌面与移动视口登录;
- 使用 `$unidesk-otel` 查询登录 `traceId`span error 数为 0。
- 密码只从 owning YAML 声明的 `sourceRef` 读取,禁止出现在 CLI 参数、日志、截图或任务报告中。
- 完整身份/RBAC、严格一致性审计和附件备份增强属于独立后续任务,不阻塞上述 MVP 验收。
## 本地验证
- `config/fixtures/pikaoa-test-target.yaml` 只用于 renderer 和 CLI 测试。
+10 -1
View File
@@ -15,7 +15,16 @@
- Gitea mirror 与 Pipelines-as-Code 是 UniDesk 运维的 CI source/trigger 服务。`config/platform-infra/gitea.yaml``config/platform-infra/pipelines-as-code.yaml` 分别拥有 mirror/webhook 与 PaC Repository/consumer 配置;repo URL、snapshot、webhook、public exposure、FRP/Caddy port、token sourceRef 和 PaC params 不得隐藏在代码特例中。
- Delivery authority resolver 必须组合上述两份 YAML,并按 consumer id、node、lane、upstream repository、branch 和 Gitea repository 精确关联。不得通过 URL 片段、repo 名称前缀或仓库专属 `if` 推断迁移状态。零匹配、多匹配和配置错误都必须返回 `unknown``mutation=false` 并停止生成或执行写操作。
- PaC consumer 的唯一正式触发是目标 source branch 的 GitHub PR merge。完整自动链固定为:GitHub PR merge -> GitHub webhook -> Gitea controlled mirror -> immutable snapshot -> Gitea webhook -> PaC/Pipelines-as-Code -> Tekton -> GitOps/Argo -> runtime 运行面。
- PR 合并后,主代理、子代理和操作者都不得用 `apply``closeout``trigger-current``refresh``sync``flush``webhook-test`、人工 PipelineRun、直接 Gitea push、本地 mirror 写入或其他补跑完成当前交付。自动链不通时必须修 owning YAML、controller 或源码,并只用修复 PR 合并产生的新正常自动事件验收。
- PR 合并后的交付边界:
- 主代理、子代理和操作者不得用 `apply``closeout``trigger-current`
`refresh``sync``flush``webhook-test`、人工 PipelineRun、
直接 Gitea push、本地 mirror 写入或其他补跑完成当前交付;
- 自动链不通时必须修 owning YAML、controller 或源码,
并只用修复 PR 合并产生的新正常自动事件验收;
- 必要的运行面调试或临时恢复可以走 `$unidesk-daddev` P2 最小可逆 patch
- patch 不得修改 source/ref authority、制造交付成功或替代终态;
- 成立的变化必须写回 owning YAML/源码并经正常 PR 自动交付;
- 临时 patch 随后撤销或由声明式交付覆盖。
- Migrated consumer 的默认 help、status、`Next``REPAIR` 只能给 `status``history``events``logs`、只读单步下钻以及本节稳定引用。CLI 必须省略 mutation command,而不是只给命令加警告文字。`unknown` authority 同样 fail-closed。
- 旧 `trigger-current``refresh` 和 mirror `sync|flush` 只允许在 owning YAML 明确解析为 `legacy-manual` 后执行,并且只在显式 `legacy-cicd``legacy-ops` scoped help 中可发现。平台 bootstrap、Secret 与配置维护属于独立职责,只在 `platform-bootstrap``platform-maintenance` scoped help 中展示,不能作为 source delivery recovery。
- `closeout` 仅保留只读历史/诊断兼容入口,并且只能从 `compatibility-diagnostics` scoped help 发现。会 POST hook test 的 `webhook-test` mutation 入口已经删除;连通性只能通过真正只读的 status、GET 与 readiness 观察,禁止制造伪 push。
+18
View File
@@ -441,6 +441,24 @@ function agentRunTektonGitopsPublishScript(spec: AgentRunLaneSpec): string {
"cd \"$root/gitops\"",
"git fetch origin \"$(params.gitops-branch)\" || true",
"if git rev-parse --verify \"refs/remotes/origin/$(params.gitops-branch)^{commit}\" >/dev/null 2>&1; then git checkout -B \"$(params.gitops-branch)\" \"refs/remotes/origin/$(params.gitops-branch)\"; else git checkout --orphan \"$(params.gitops-branch)\"; git rm -rf . >/dev/null 2>&1 || true; fi",
"incoming_source_commit='$(params.revision)'",
"source_branch='$(params.source-branch)'",
"branch_source_commit=$(git -C \"$root/repo\" rev-parse --verify \"refs/remotes/origin/${source_branch}^{commit}\")",
"gitops_source_commit=$(node -e 'try { const value = JSON.parse(require(\"node:fs\").readFileSync(\"source.json\", \"utf8\")).sourceCommit; if (typeof value === \"string\") process.stdout.write(value); } catch {}')",
"stale_reason=''",
"if [ \"$incoming_source_commit\" != \"$branch_source_commit\" ]; then stale_reason='stale-source-revision'; fi",
"if [ -z \"$stale_reason\" ] && [ -n \"$gitops_source_commit\" ] && [ \"$incoming_source_commit\" != \"$gitops_source_commit\" ]; then",
" if ! git -C \"$root/repo\" cat-file -e \"${gitops_source_commit}^{commit}\" 2>/dev/null; then stale_reason='gitops-source-order-unverifiable';",
" elif git -C \"$root/repo\" merge-base --is-ancestor \"$incoming_source_commit\" \"$gitops_source_commit\"; then stale_reason='stale-gitops-source-revision';",
" elif ! git -C \"$root/repo\" merge-base --is-ancestor \"$gitops_source_commit\" \"$incoming_source_commit\"; then stale_reason='divergent-gitops-source-revision'; fi",
"fi",
"if [ -n \"$stale_reason\" ]; then",
" gitops_commit=$(git rev-parse HEAD)",
" INCOMING_SOURCE_COMMIT=\"$incoming_source_commit\" CURRENT_SOURCE_COMMIT=\"$branch_source_commit\" GITOPS_SOURCE_COMMIT=\"$gitops_source_commit\" GITOPS_COMMIT=\"$gitops_commit\" STALE_REASON=\"$stale_reason\" node <<'NODE'",
"console.log(JSON.stringify({ ok: true, status: 'succeeded', phase: 'gitops-publish', reason: process.env.STALE_REASON, warning: true, blocking: false, changed: false, stale: true, gitopsCommit: process.env.GITOPS_COMMIT, sourceCommit: process.env.INCOMING_SOURCE_COMMIT, currentSourceCommit: process.env.CURRENT_SOURCE_COMMIT, gitopsSourceCommit: process.env.GITOPS_SOURCE_COMMIT || null, valuesPrinted: false }));",
"NODE",
" exit 0",
"fi",
"git rm -rf --ignore-unmatch \"$(params.gitops-root)\" \"$(params.artifact-catalog)\" source.json >/dev/null 2>&1 || true",
"rm -rf \"$(params.gitops-root)\" \"$(params.artifact-catalog)\" source.json",
"TEMPLATES_B64=\"$templates_b64\" BUILD_RESULT=\"$build_result\" node <<'NODE'",
@@ -21,11 +21,13 @@ test("shared stage injects owning YAML OTel values for all three PaC repositorie
renderedRootExpression: "'/workspace/rendered'",
});
expect(stage).toContain("feature-config-schema-validation");
expect(stage).toContain("cicd.feature_config.schema");
expect(stage).toContain("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT='http://otel-collector.platform-infra.svc.cluster.local:4318/v1/traces'");
expect(stage).toContain("OTEL_TRACES_SAMPLER_ARG='1'");
expect(stage).toContain("OTEL_EXPORTER_TIMEOUT_MS='300'");
expect(stage).toContain("NODE_UNIDESK_AJV2020_BUNDLE");
expect(stage).toContain("base64 -d | gzip -d");
expect(stage).toContain("NODE_UNIDESK_FEATURE_CONFIG_SCHEMA");
expect(stage).not.toContain("cicd.feature_config.schema");
expect(stage).not.toContain("NODE_UNIDESK_AJV2020_BUNDLE");
expect(stage).not.toContain("process.env.webProbeSentinel");
}
});
@@ -112,7 +114,9 @@ test("AgentRun normal GitOps publish task contains the shared warning-only stage
expect(scripts).toContain("feature-config-schema-validation");
expect(scripts).toContain("feature-config-validator-process-failure");
expect(scripts).toContain("OTEL_EXPORTER_TIMEOUT_MS='300'");
expect(scripts).toContain("NODE_UNIDESK_AJV2020_BUNDLE");
expect(scripts).toContain("base64 -d | gzip -d");
expect(scripts).toContain("NODE_UNIDESK_FEATURE_CONFIG_SCHEMA");
expect(scripts).not.toContain("NODE_UNIDESK_AJV2020_BUNDLE");
});
test("repo-owned schema is Draft 2020-12 with one canonical feature property", () => {
+23 -10
View File
@@ -1,10 +1,13 @@
import { readFileSync } from "node:fs";
import { gzipSync } from "node:zlib";
import { rootPath } from "./config";
import { loadCicdOtelRuntimeConfig } from "../native/cicd/otel-runtime-config";
const validatorSource = readFileSync(rootPath("scripts/native/cicd/feature-config-schema-warning.mjs"), "utf8").trimEnd();
const ajv2020BundleSource = readFileSync(rootPath("scripts/vendor/ajv-dist/8.17.1/ajv2020.min.js"), "utf8").trimEnd();
const validatorGzipBase64 = gzipBase64(validatorSource);
const ajv2020GzipBase64 = gzipBase64(ajv2020BundleSource);
export interface PacFeatureConfigSchemaStageOptions {
readonly repositoryId: string;
@@ -26,14 +29,21 @@ export function renderPacFeatureConfigSchemaStage(options: PacFeatureConfigSchem
`export OTEL_SERVICE_NAME=${shellSingle(otel.serviceName)}`,
`export OTEL_TRACES_SAMPLER_ARG=${shellSingle(String(otel.samplingRatio))}`,
`export OTEL_EXPORTER_TIMEOUT_MS=${shellSingle(String(otel.timeoutMs))}`,
"feature_config_validator_base=''",
"feature_config_validator=''",
"if feature_config_validator_base=\"$(mktemp \"${TMPDIR:-/tmp}/unidesk-feature-config-validator.XXXXXX\" 2>/dev/null)\"; then",
" feature_config_validator=\"${feature_config_validator_base}.mjs\"",
" if ! printf '%s' " + shellSingle(validatorGzipBase64) + " | base64 -d | gzip -d >\"$feature_config_validator\"; then",
" rm -f \"$feature_config_validator_base\" \"$feature_config_validator\" || true",
" feature_config_validator=''",
" fi",
" rm -f \"$feature_config_validator_base\" || true",
"fi",
...(materializeBundle
? [
"feature_config_ajv_bundle=''",
"if feature_config_ajv_bundle=\"$(mktemp \"${TMPDIR:-/tmp}/unidesk-ajv2020.XXXXXX\" 2>/dev/null)\"; then",
" if ! cat >\"$feature_config_ajv_bundle\" <<'NODE_UNIDESK_AJV2020_BUNDLE'",
ajv2020BundleSource,
"NODE_UNIDESK_AJV2020_BUNDLE",
" then",
" if ! printf '%s' " + shellSingle(ajv2020GzipBase64) + " | base64 -d | gzip -d >\"$feature_config_ajv_bundle\"; then",
" rm -f \"$feature_config_ajv_bundle\" || true",
" feature_config_ajv_bundle=''",
" fi",
@@ -41,17 +51,16 @@ export function renderPacFeatureConfigSchemaStage(options: PacFeatureConfigSchem
"export UNIDESK_AJV2020_BUNDLE=\"${feature_config_ajv_bundle:-${TMPDIR:-/tmp}/unidesk-ajv2020-unavailable}\"",
]
: [`export UNIDESK_AJV2020_BUNDLE=${options.ajvBundleExpression}`]),
"export UNIDESK_FEATURE_CONFIG_VALIDATOR=\"${feature_config_validator:-${TMPDIR:-/tmp}/unidesk-feature-config-validator-unavailable.mjs}\"",
"if ! node --input-type=module <<'NODE_UNIDESK_FEATURE_CONFIG_SCHEMA'",
validatorSource,
"await runFeatureConfigSchemaValidation({",
" repoDir: process.env.UNIDESK_FEATURE_CONFIG_REPO_DIR,",
" renderedRoot: process.env.UNIDESK_FEATURE_CONFIG_RENDERED_ROOT,",
" consumer: process.env.UNIDESK_PAC_CONSUMER,",
"});",
"import { pathToFileURL } from 'node:url';",
"const validator = await import(pathToFileURL(process.env.UNIDESK_FEATURE_CONFIG_VALIDATOR).href);",
"await validator.runFeatureConfigSchemaValidation({ repoDir: process.env.UNIDESK_FEATURE_CONFIG_REPO_DIR, renderedRoot: process.env.UNIDESK_FEATURE_CONFIG_RENDERED_ROOT, consumer: process.env.UNIDESK_PAC_CONSUMER });",
"NODE_UNIDESK_FEATURE_CONFIG_SCHEMA",
"then",
" printf '{\"event\":\"feature-config-schema-validation\",\"warning\":true,\"blocking\":false,\"code\":\"feature-config-validator-process-failure\",\"mutation\":false,\"valuesPrinted\":false}\\n' >&2",
"fi",
"if [ -n \"$feature_config_validator\" ]; then rm -f \"$feature_config_validator\" || true; fi",
...(materializeBundle ? ["if [ -n \"$feature_config_ajv_bundle\" ]; then rm -f \"$feature_config_ajv_bundle\" || true; fi"] : []),
"if command -v ci_timing_emit >/dev/null 2>&1; then",
" ci_timing_emit feature-config-schema-validation succeeded \"$feature_config_schema_started_ms\"",
@@ -64,3 +73,7 @@ export function renderPacFeatureConfigSchemaStage(options: PacFeatureConfigSchem
function shellSingle(value: string): string {
return `'${value.replaceAll("'", `'"'"'`)}'`;
}
function gzipBase64(value: string): string {
return gzipSync(Buffer.from(value, "utf8"), { level: 9 }).toString("base64");
}
@@ -4,7 +4,7 @@ import { resolve } from "node:path";
import type { GiteaConfig, GiteaMirrorRepository } from "./platform-infra-gitea-config";
import { renderMirrorBootstrap } from "./platform-infra-gitea-render";
import { pacBootstrapYamlMatchFailure, projectPacBootstrapResult, renderPacBootstrap, resolvePacBootstrapMirrorRepository } from "./platform-infra-pipelines-as-code-bootstrap";
import { parsePacConfigDocument, readPacConfig, runPlatformInfraPipelinesAsCodeCommand, validatePacConfig, validPacConsumers } from "./platform-infra-pipelines-as-code";
import { parsePacConfigDocument, readPacConfig, renderPacReleaseManifest, runPlatformInfraPipelinesAsCodeCommand, validatePacConfig, validPacConsumers } from "./platform-infra-pipelines-as-code";
import { materializeYamlComposition } from "./yaml-composition";
const mirror: GiteaMirrorRepository = {
@@ -185,6 +185,38 @@ test("PaC apply checks the Gitea repository before dry-run return and cluster mu
expect(statusAction).toContain("blocking: false");
});
test("PaC release manifest renders YAML-owned watcher startup backlog probes", () => {
const pac = readPacConfig({ consumerId: "hwlab-nc01-v03", selectDefault: true });
const manifest = renderPacReleaseManifest(pac, `apiVersion: apps/v1
kind: Deployment
metadata:
name: pipelines-as-code-watcher
namespace: pipelines-as-code
spec:
template:
spec:
containers:
- name: pac-watcher
image: example.invalid/pac-watcher:v0
readinessProbe:
httpGet:
path: /live
port: probes
---
apiVersion: v1
kind: Service
metadata:
name: pipelines-as-code-watcher
namespace: pipelines-as-code
`);
const documents = manifest.split(/^---\s*$/mu).map((item) => item.trim()).filter(Boolean).map((item) => Bun.YAML.parse(item) as any);
const watcher = documents[0].spec.template.spec.containers[0];
expect(watcher.startupProbe).toEqual(pac.release.workloads.watcher.startupProbe);
expect(watcher.readinessProbe).toEqual(pac.release.workloads.watcher.readinessProbe);
expect(watcher.livenessProbe).toEqual(pac.release.workloads.watcher.livenessProbe);
expect(documents[1].kind).toBe("Service");
});
test("platform bootstrap help advertises the composite entry before low-level apply", async () => {
const help = await runPlatformInfraPipelinesAsCodeCommand({} as never, ["help", "platform-bootstrap"]);
const usage = (help as Record<string, unknown>).usage as string[];
@@ -71,6 +71,26 @@ interface PacTarget {
enabled: boolean;
}
interface PacHttpProbe {
httpGet: {
path: string;
port: string;
};
initialDelaySeconds: number;
periodSeconds: number;
timeoutSeconds: number;
failureThreshold: number;
successThreshold: number;
}
interface PacWorkloadProbes {
deploymentName: string;
containerName: string;
startupProbe: PacHttpProbe;
readinessProbe: PacHttpProbe;
livenessProbe: PacHttpProbe;
}
export interface PacConfig {
version: number;
kind: "platform-infra-pipelines-as-code";
@@ -104,6 +124,9 @@ export interface PacConfig {
controllerServiceName: string;
controllerServicePort: number;
waitTimeoutSeconds: number;
workloads: {
watcher: PacWorkloadProbes;
};
};
deliveryProvenance: {
version: string;
@@ -474,6 +497,8 @@ export function parsePacConfigDocument(
selection: { readonly consumerId?: string | null; readonly selectDefault?: boolean } = {},
): PacConfig {
const release = y.objectField(root, "release", "");
const releaseWorkloads = y.objectField(release, "workloads", "release");
const watcherWorkload = y.objectField(releaseWorkloads, "watcher", "release.workloads");
const deliveryProvenance = y.objectField(root, "deliveryProvenance", "");
const provenanceController = y.objectField(deliveryProvenance, "controller", "deliveryProvenance");
const provenanceAdmission = y.objectField(deliveryProvenance, "admission", "deliveryProvenance");
@@ -542,6 +567,9 @@ export function parsePacConfigDocument(
controllerServiceName: y.kubernetesNameField(release, "controllerServiceName", "release"),
controllerServicePort: y.portField(release, "controllerServicePort", "release"),
waitTimeoutSeconds: positiveInteger(release, "waitTimeoutSeconds", "release"),
workloads: {
watcher: parsePacWorkloadProbes(watcherWorkload, "release.workloads.watcher"),
},
},
deliveryProvenance: {
version: y.stringField(deliveryProvenance, "version", "deliveryProvenance"),
@@ -1714,7 +1742,35 @@ async function fetchReleaseManifest(pac: PacConfig): Promise<string> {
if (!response.ok) throw new Error(`failed to fetch ${pac.release.manifestUrl}: HTTP ${response.status}`);
const text = await response.text();
if (!text.includes("repositories.pipelinesascode.tekton.dev")) throw new Error(`${pac.release.manifestUrl} did not contain the Repository CRD`);
return text;
return renderPacReleaseManifest(pac, text);
}
export function renderPacReleaseManifest(pac: PacConfig, source: string): string {
const workload = pac.release.workloads.watcher;
let matched = false;
const documents = source
.split(/^---\s*$/mu)
.map((item) => item.trim())
.filter(Boolean)
.map((item) => Bun.YAML.parse(item) as unknown)
.map((item) => {
const document = record(item);
const metadata = record(document.metadata);
if (document.kind !== "Deployment" || metadata.name !== workload.deploymentName || metadata.namespace !== pac.release.namespace) return document;
const spec = record(document.spec);
const template = record(spec.template);
const podSpec = record(template.spec);
const containers = arrayRecords(podSpec.containers);
const container = containers.find((candidate) => candidate.name === workload.containerName);
if (container === undefined) throw new Error(`${configLabel}.release.workloads.watcher.containerName did not match ${workload.deploymentName}`);
container.startupProbe = structuredClone(workload.startupProbe);
container.readinessProbe = structuredClone(workload.readinessProbe);
container.livenessProbe = structuredClone(workload.livenessProbe);
matched = true;
return document;
});
if (!matched) throw new Error(`${configLabel}.release.workloads.watcher.deploymentName did not match the release manifest`);
return `${documents.map((item) => Bun.YAML.stringify(item).trim()).join("\n---\n")}\n`;
}
function remoteScript(action: "apply" | "status" | "history" | "debug-step", pac: PacConfig, target: PacTarget, repository: PacRepository, consumer: PacConsumer, options: ApplyOptions | HistoryOptions, secrets: SecretMaterial, releaseManifest: string, historyConsumers: PacConsumer[] = [consumer]): string {
@@ -3187,6 +3243,31 @@ function positiveInteger(obj: Record<string, unknown>, key: string, path: string
return value;
}
function parsePacHttpProbe(obj: Record<string, unknown>, path: string): PacHttpProbe {
const httpGet = y.objectField(obj, "httpGet", path);
return {
httpGet: {
path: y.stringField(httpGet, "path", `${path}.httpGet`),
port: y.stringField(httpGet, "port", `${path}.httpGet`),
},
initialDelaySeconds: positiveInteger(obj, "initialDelaySeconds", path),
periodSeconds: positiveInteger(obj, "periodSeconds", path),
timeoutSeconds: positiveInteger(obj, "timeoutSeconds", path),
failureThreshold: positiveInteger(obj, "failureThreshold", path),
successThreshold: positiveInteger(obj, "successThreshold", path),
};
}
function parsePacWorkloadProbes(obj: Record<string, unknown>, path: string): PacWorkloadProbes {
return {
deploymentName: y.kubernetesNameField(obj, "deploymentName", path),
containerName: y.kubernetesNameField(obj, "containerName", path),
startupProbe: parsePacHttpProbe(y.objectField(obj, "startupProbe", path), `${path}.startupProbe`),
readinessProbe: parsePacHttpProbe(y.objectField(obj, "readinessProbe", path), `${path}.readinessProbe`),
livenessProbe: parsePacHttpProbe(y.objectField(obj, "livenessProbe", path), `${path}.livenessProbe`),
};
}
function urlField(obj: Record<string, unknown>, key: string, path: string): string {
const value = y.stringField(obj, key, path);
const parsed = new URL(value);