diff --git a/.agents/skills/unidesk-cicd/SKILL.md b/.agents/skills/unidesk-cicd/SKILL.md index bde8c7b7..faeeb049 100644 --- a/.agents/skills/unidesk-cicd/SKILL.md +++ b/.agents/skills/unidesk-cicd/SKILL.md @@ -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 或跨对象校验; diff --git a/.agents/skills/unidesk-daddev/SKILL.md b/.agents/skills/unidesk-daddev/SKILL.md new file mode 100644 index 00000000..c807e0a0 --- /dev/null +++ b/.agents/skills/unidesk-daddev/SKILL.md @@ -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/`,后续编辑、验证、提交、推送和受控 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 artifact,CD 只消费 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-poll;CLI 先提交,后续短查询 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)。紧凑流程不足以支撑任务时读取该文件。 diff --git a/.agents/skills/unidesk-daddev/references/details.md b/.agents/skills/unidesk-daddev/references/details.md new file mode 100644 index 00000000..0bb96e6d --- /dev/null +++ b/.agents/skills/unidesk-daddev/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 :/ sh -- 'cat AGENTS.md'` + 相关 `docs/reference/*` | 取得目标仓库的当前任务约束(不靠主 server 记忆) | +| 1.2 grep 关键字定位 | `rg "" --type ts --type mjs` 等 | 找到嫌疑文件 + 行号 | +| 1.3 实测复现 | `trans sh -- ''` / 原入口 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 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 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 envelope;P3 再把同一逻辑用源码路径落回 worktree,并在 issue 证据里说明路径映射和逻辑等价关系。 + - 禁止绕路:不要从 host/worktree 取 `git diff`、改路径、再用复杂 shell quoting 拼到 pod;文本热修优先走 `trans <目标运行面 route> apply-patch`,第一个 route token 直接定位到目标 pod / workspace。 + +--- + +### P3:持久化交付(落回 source truth + 项目适配器) + +目标:把 P2 验证有效的修复落到目标项目声明的 source truth,并通过项目适配器完成交付。结束时必须能给出可审计 provenance:commit / 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/`,后续只在该 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、merge;GitHub 写走项目受控 CLI | commit / PR / merge 可追溯 | +| 3.6 受控交付 | 按项目适配器执行 CI/CD、artifact、rollout、deploy 或跳过 CI/CD(pr-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 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/` / 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 sh -- ''` | 每条"期望 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。 + - scope:out-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) + +- 变更纪律 + - worktree:P3 默认先按项目规则更新 fixed repo,再从最新 remote/base 创建独立 `.worktree/` / 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 规则等只在对应项目适配器内生效,不要复制到其他项目。 + - 基础设施缺陷混入业务 scope:monitor / cron / CLI 字段缺失 / 可见性不足等基础设施问题可先补最小能力解阻;长期恢复应单独 issue 跟踪,不能混入当前业务 PR。 +- 证据反模式 + - 无证据 close:在 issue 评论里说"应该修好了"不是证据;没有 trace / job id / argv / 截图 = 不能 close issue;证据必须是可被独立审计员复现的命令输出原文。 + - telemetry dump:只有 job id、trace、commit、PipelineRun、JSON 片段而没有自然语言正文说明,读者无法理解决策;必须先写语义化正文,再列审计证据。 + - session memory:agent session memory / 历史对话不可审计;证据必须落在 issue comment / git commit / PipelineRun / k8s object 这类可被外部引用的位置。 diff --git a/.agents/skills/unidesk-hwpod-ops/SKILL.md b/.agents/skills/unidesk-hwpod-ops/SKILL.md index 387b515c..13b47bf8 100644 --- a/.agents/skills/unidesk-hwpod-ops/SKILL.md +++ b/.agents/skills/unidesk-hwpod-ops/SKILL.md @@ -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` 保持短连接;图形进程不得作为透传子进程长挂。 diff --git a/.agents/skills/unidesk-hwpod-ops/references/python-ui-node.md b/.agents/skills/unidesk-hwpod-ops/references/python-ui-node.md index c78ef557..4d7dd76a 100644 --- a/.agents/skills/unidesk-hwpod-ops/references/python-ui-node.md +++ b/.agents/skills/unidesk-hwpod-ops/references/python-ui-node.md @@ -127,4 +127,4 @@ $root = Join-Path $HOME ".hwlab" - 比较 nodeId、云端地址、资源绑定和活动硬件任务; - 可能中断硬件操作的变更必须有明确维护窗口; - - 变更必须遵循 `$dad-dev` 流程。 + - 变更必须遵循 `$unidesk-daddev` 流程。 diff --git a/.agents/skills/unidesk-kafka/SKILL.md b/.agents/skills/unidesk-kafka/SKILL.md index a60c4250..81ad7665 100644 --- a/.agents/skills/unidesk-kafka/SKILL.md +++ b/.agents/skills/unidesk-kafka/SKILL.md @@ -142,5 +142,5 @@ bun scripts/cli.ts web-probe observe status \ - 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。 diff --git a/.agents/skills/unidesk-nginx/SKILL.md b/.agents/skills/unidesk-nginx/SKILL.md index 87ef18de..c6c25d3d 100644 --- a/.agents/skills/unidesk-nginx/SKILL.md +++ b/.agents/skills/unidesk-nginx/SKILL.md @@ -20,7 +20,7 @@ description: >- - PK01 边界:`docs/reference/pk01.md`。 涉及 YAML 职责拆分时同时加载 `$unidesk-ymalops`。 -涉及跨 host 现场修改或验证时同时加载 `$dad-dev` 与 `$unidesk-trans`。 +涉及跨 host 现场修改或验证时同时加载 `$unidesk-daddev` 与 `$unidesk-trans`。 ## 高频入口 diff --git a/.agents/skills/unidesk-selfmedia/SKILL.md b/.agents/skills/unidesk-selfmedia/SKILL.md index c6b03ab7..ef371c2b 100644 --- a/.agents/skills/unidesk-selfmedia/SKILL.md +++ b/.agents/skills/unidesk-selfmedia/SKILL.md @@ -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`。 ## 详细入口 diff --git a/.agents/skills/unidesk-subagent/SKILL.md b/.agents/skills/unidesk-subagent/SKILL.md index 5254c607..945020fb 100644 --- a/.agents/skills/unidesk-subagent/SKILL.md +++ b/.agents/skills/unidesk-subagent/SKILL.md @@ -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 和验收入口的工程化任务,必须在恢复期间立即并行派发,不得全部排到恢复之后串行执行。 diff --git a/.agents/skills/unidesk-subagent/references/gh-workflow.md b/.agents/skills/unidesk-subagent/references/gh-workflow.md index ebc6692a..93f55d8d 100644 --- a/.agents/skills/unidesk-subagent/references/gh-workflow.md +++ b/.agents/skills/unidesk-subagent/references/gh-workflow.md @@ -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 或确认其已被声明式交付覆盖。 - 恢复期间降低并发必须有具名原因,例如共享生产对象、同一状态机、权限边界或运行面容量;原因解除后立即重新计算可安全并发窗口。 ## 模型与思考等级 diff --git a/.agents/skills/unidesk-trans/SKILL.md b/.agents/skills/unidesk-trans/SKILL.md index eb8c4350..9d3eef1b 100644 --- a/.agents/skills/unidesk-trans/SKILL.md +++ b/.agents/skills/unidesk-trans/SKILL.md @@ -27,7 +27,12 @@ trans D601:win/c/test git exact-commit gc-auto --confirm trans gh:/owner/repo/issue/ 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 --lane --confirm` 和 `source-workspace status`;不要把裸 `trans : 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/` WSL host workspace 由 root 透传执行时,CLI 自动把 workspace owner uid 桥接到进程级 `SUDO_UID`,让 Git 信任 Windows 挂载目录而不写全局 `safe.directory`;非 Git 命令和普通 Linux workspace 不改变所有权信任。 - Windows route 的 `win` 是 route plane,operation 直接写 `ps`、`cmd`、`git` 或只读 fs 操作 `pwd|ls|cat|head|tail|stat|wc|rg`;不要写成 `trans D601:win/... win ps`,也不要把 POSIX shell 当 Windows shell。 - Windows `rg`: diff --git a/.agents/skills/unidesk-trans/references/operations.md b/.agents/skills/unidesk-trans/references/operations.md index 6d66823b..73981206 100644 --- a/.agents/skills/unidesk-trans/references/operations.md +++ b/.agents/skills/unidesk-trans/references/operations.md @@ -31,3 +31,8 @@ - 验证独立 worktree 时显式设置 `UNIDESK_TRANS_REPO_ROOT=`。 - 普通 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 无残留。 diff --git a/.agents/skills/unidesk-trans/references/remote-experiment-smoke.md b/.agents/skills/unidesk-trans/references/remote-experiment-smoke.md new file mode 100644 index 00000000..474d71b1 --- /dev/null +++ b/.agents/skills/unidesk-trans/references/remote-experiment-smoke.md @@ -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 :/tmp sh <<'SH' +set -eu +work_dir= +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 : apply-patch <<'PATCH' +*** Begin Patch +*** Add File: jobs/.sh ++#!/bin/sh ++set +e ++ ++ ++code=$? ++printf '%s\n' "$code" > /absolute/work-dir/jobs/.exit ++touch /absolute/work-dir/jobs/.done ++exit "$code" +*** End Patch +PATCH +``` + +job 脚本只能引用任务已确认的绝对目录、资源前缀和公开依赖身份。需要 Secret 的任务不使用本模板自行读取运行面值。 + +### 3. Submit + +submit 只负责清理旧状态、后台启动和返回 PID,不等待长命令: + +```bash +trans : sh <<'SH' +set -eu +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 : sh <<'SH' +set -eu +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 : sh <<'SH' +set -eu +work_dir=/absolute/work-dir +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 中无界扩展。 diff --git a/.agents/skills/unidesk-ymalops/SKILL.md b/.agents/skills/unidesk-ymalops/SKILL.md index 689b5281..bf81a5cd 100644 --- a/.agents/skills/unidesk-ymalops/SKILL.md +++ b/.agents/skills/unidesk-ymalops/SKILL.md @@ -28,6 +28,11 @@ description: UniDesk YAML-first 运维正规化技能。用户提到 ymal-first/ - 先执行 `git status --short --branch` 和 `git pull --ff-only origin master`;保留并绕开无关并行修改。 - 源码、配置、部署类正规化默认在独立 `.worktree/` 中做;轻量 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 YAML;root YAML 只保存归属和 `configRefs`/path 引用,不承载全部细节。 diff --git a/AGENTS.md b/AGENTS.md index 61f61f10..fb0027ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 或构建通过不能替代原入口验证。 diff --git a/config/agentrun.yaml b/config/agentrun.yaml index a6d7987f..6df6b161 100644 --- a/config/agentrun.yaml +++ b/config/agentrun.yaml @@ -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 diff --git a/config/platform-infra/gitea.yaml b/config/platform-infra/gitea.yaml index 6d6c1f72..d6dd21be 100644 --- a/config/platform-infra/gitea.yaml +++ b/config/platform-infra/gitea.yaml @@ -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: diff --git a/config/platform-infra/pipelines-as-code.yaml b/config/platform-infra/pipelines-as-code.yaml index 35b516f1..e2572e57 100644 --- a/config/platform-infra/pipelines-as-code.yaml +++ b/config/platform-infra/pipelines-as-code.yaml @@ -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" diff --git a/docs/MDTODO/agentrun-dev-production-lanes.md b/docs/MDTODO/agentrun-dev-production-lanes.md new file mode 100644 index 00000000..e18ee1a5 --- /dev/null +++ b/docs/MDTODO/agentrun-dev-production-lanes.md @@ -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 lane(runtime.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→v02,prod→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)。 diff --git a/docs/MDTODO/agentrun-runtime-reliability.md b/docs/MDTODO/agentrun-runtime-reliability.md index 05551c4e..f5cef2d4 100644 --- a/docs/MDTODO/agentrun-runtime-reliability.md +++ b/docs/MDTODO/agentrun-runtime-reliability.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` AipodSpec;PaC 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)。 diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R1.1_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.1_Task_Report.md new file mode 100644 index 00000000..209af054 --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.1_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` diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R1.2_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.2_Task_Report.md new file mode 100644 index 00000000..45b31f52 --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.2_Task_Report.md @@ -0,0 +1,11 @@ +# R1.2 任务报告 + +## 结论 + +NC01 k3s 已创建 `agentrun-release` Namespace,status=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` diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R1.3_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.3_Task_Report.md new file mode 100644 index 00000000..61a61382 --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.3_Task_Report.md @@ -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 流量。 diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R1.4_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.4_Task_Report.md new file mode 100644 index 00000000..08aae1a5 --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R1.4_Task_Report.md @@ -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 均隔离。 diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R1_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R1_Task_Report.md new file mode 100644 index 00000000..8f8ab64c --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R1_Task_Report.md @@ -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 无回归 + +## 未做(后续 R2–R7) + +模板参数化、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 +``` diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R3_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R3_Task_Report.md new file mode 100644 index 00000000..a61fa194 --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R3_Task_Report.md @@ -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 对象已 apply:Tekton SA/Role/Pipeline、Argo AppProject(Application 因 gitops 分支未解析保持 ComparisonError,已去掉 automated sync 防误伤 bootstrap) + +## 未完成 + +- agentrun 仓创建 `release` / `nc01-release-gitops` 与 `.tekton/agentrun-nc01-release.yaml` +- PaC Repository CR / webhook 在集群侧 bootstrap(consumer 权威已可解析) diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R4_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R4_Task_Report.md new file mode 100644 index 00000000..1b4877ba --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R4_Task_Report.md @@ -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` diff --git a/docs/MDTODO/details/agentrun-dev-production-lanes/R5_Task_Report.md b/docs/MDTODO/details/agentrun-dev-production-lanes/R5_Task_Report.md new file mode 100644 index 00000000..c4ab0ad3 --- /dev/null +++ b/docs/MDTODO/details/agentrun-dev-production-lanes/R5_Task_Report.md @@ -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 推进。 diff --git a/docs/MDTODO/details/agentrun-runtime-reliability/R5.10_Task_Report.md b/docs/MDTODO/details/agentrun-runtime-reliability/R5.10_Task_Report.md new file mode 100644 index 00000000..02b5e5d5 --- /dev/null +++ b/docs/MDTODO/details/agentrun-runtime-reliability/R5.10_Task_Report.md @@ -0,0 +1,50 @@ +# R5.10 P0 事故复盘 + +## 结论 + +本次事故不是生产 lane writer 修改开发环境,也不是数据库迁移依赖。真正根因是一个此前被 admission 阻塞的旧 AgentRun PaC PipelineRun,在较新 `b258520` 已成功发布后才开始执行,并以较新的 GitOps commit 为父提交,把 `sourceCommit` 逆序写回旧值 `0ca2c73`。Argo 正常同步了错误 desired,manager 因此重新加载旧 `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 KiB,init 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 pass,0 fail;未运行 Vitest。 +- AgentRun source artifact:aligned=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 漂移关闭功能或阻塞滚动。 diff --git a/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9.13.1_Task_Report.md b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9.13.1_Task_Report.md new file mode 100644 index 00000000..57e61a0f --- /dev/null +++ b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9.13.1_Task_Report.md @@ -0,0 +1,3 @@ +# R1.5.9.13.1 任务报告 + +PaC admission provenance 漂移已按用户要求降级为非阻塞 warning。共享 Repository 与独立 consumer 可以完成 bootstrap,正常产品 PR merge 产生的 PipelineRun 不再被旧一致性门禁阻塞。 diff --git a/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9.13_Task_Report.md b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9.13_Task_Report.md new file mode 100644 index 00000000..0548724b --- /dev/null +++ b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9.13_Task_Report.md @@ -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、数据库和完整合同/发票主路径均已验收。 diff --git a/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9_Task_Report.md b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9_Task_Report.md new file mode 100644 index 00000000..0baae881 --- /dev/null +++ b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.5.9_Task_Report.md @@ -0,0 +1,3 @@ +# R1.5.9 任务报告 + +现有 NC01 k3s 已建立固定 pikaoa-test namespace,使用 PK01 host PostgreSQL 独立测试库与角色,并以 YAML 声明 NodePort 32080。CLI 单步部署、测试 CI/CD、桌面与移动 Web、合同多版本、发票关联及废弃、OTel 和 Prometheus 均已通过真实验收。 diff --git a/docs/MDTODO/details/pikaoa-enterprise-platform/R1.6_Task_Report.md b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.6_Task_Report.md new file mode 100644 index 00000000..247fbb22 --- /dev/null +++ b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.6_Task_Report.md @@ -0,0 +1,3 @@ +# R1.6 任务报告 + +双仓生产交付改动已审核合并。产品 release 的正常 PR merge 自动触发生产 PipelineRun,API、Worker、Web 镜像构建与 GitOps 发布全部成功;未人工创建 PipelineRun。 diff --git a/docs/MDTODO/details/pikaoa-enterprise-platform/R1.7_Task_Report.md b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.7_Task_Report.md new file mode 100644 index 00000000..b082e88f --- /dev/null +++ b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.7_Task_Report.md @@ -0,0 +1,3 @@ +# R1.7 任务报告 + +生产原入口 https://oa.pikapython.com 已完成验收:TLS 与首页返回 200,管理员公网登录成功;员工、模块、伙伴、合同、发票与审计接口可用。公网写入验收完成甲方分类、伙伴、合同 v1/v2、关联发票与废弃,全部资源 ID 唯一且版本关系正确。桌面与 390x844 移动视口登录工作台通过且无页面或控制台错误。NC01 API、Web、Worker、FRPC 均 Ready 1/1,Argo 为 Synced/Healthy;Prometheus 生产 API/Worker 数据库就绪值均为 1,登录 trace 共 18 spans、0 error。 diff --git a/docs/MDTODO/details/pikaoa-enterprise-platform/R1.8.1_Task_Report.md b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.8.1_Task_Report.md new file mode 100644 index 00000000..a2838ac5 --- /dev/null +++ b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.8.1_Task_Report.md @@ -0,0 +1,3 @@ +# R1.8.1 任务报告 + +已纠正 PikaOA 长期参考中“生产尚未 bootstrap”的过时事实,补充 release source authority、生产 namespace、PK01 数据库、fresh initializer、公网入口及非阻塞门禁边界。新增运行面、Argo、公网、Prometheus、桌面/移动 Web 与 OTel 的最短只读验收路径,使后续同类核查无需重新搜索生产状态和入口。 diff --git a/docs/MDTODO/details/pikaoa-enterprise-platform/R1.8_Task_Report.md b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.8_Task_Report.md new file mode 100644 index 00000000..07eb6bfc --- /dev/null +++ b/docs/MDTODO/details/pikaoa-enterprise-platform/R1.8_Task_Report.md @@ -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按保护规则保留。 diff --git a/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R4.1.1_Task_Report.md b/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R4.1.1_Task_Report.md index a6720ec1..dbd24b02 100644 --- a/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R4.1.1_Task_Report.md +++ b/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R4.1.1_Task_Report.md @@ -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 或删除历史业务对象。 diff --git a/docs/MDTODO/details/trans-remote-experiment-smoke/R1_Task_Report.md b/docs/MDTODO/details/trans-remote-experiment-smoke/R1_Task_Report.md new file mode 100644 index 00000000..78d0877d --- /dev/null +++ b/docs/MDTODO/details/trans-remote-experiment-smoke/R1_Task_Report.md @@ -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。 diff --git a/docs/MDTODO/pikaoa-enterprise-platform.md b/docs/MDTODO/pikaoa-enterprise-platform.md index 02b6cb97..f110dd91 100644 --- a/docs/MDTODO/pikaoa-enterprise-platform.md +++ b/docs/MDTODO/pikaoa-enterprise-platform.md @@ -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)。 diff --git a/docs/MDTODO/trans-remote-experiment-smoke.md b/docs/MDTODO/trans-remote-experiment-smoke.md new file mode 100644 index 00000000..d40f1121 --- /dev/null +++ b/docs/MDTODO/trans-remote-experiment-smoke.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)。 diff --git a/docs/reference/agent-instruction-hygiene.md b/docs/reference/agent-instruction-hygiene.md index 61188a94..a5dcba82 100644 --- a/docs/reference/agent-instruction-hygiene.md +++ b/docs/reference/agent-instruction-hygiene.md @@ -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`。 diff --git a/docs/reference/agentrun.md b/docs/reference/agentrun.md index c3b2e50d..746fe5af 100644 --- a/docs/reference/agentrun.md +++ b/docs/reference/agentrun.md @@ -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 runtime(manager 已上线;内网 ClusterIP) ``` +- production 内网入口:`http://agentrun-mgr.agentrun-release.svc.cluster.local:8080` +- production DB:host 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 diff --git a/docs/reference/devops-hygiene.md b/docs/reference/devops-hygiene.md index 3d45a296..9ea44f7a 100644 --- a/docs/reference/devops-hygiene.md +++ b/docs/reference/devops-hygiene.md @@ -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/`,后续编辑、验证、提交、push 和受控 CLI 写操作都在该 worktree 内完成。UniDesk 任务 worktree 必须通过 `bun scripts/cli.ts dev-env worktree add .worktree/ --branch [--from origin/master]` 创建,让 `.worktreecopy` 白名单内的本地配置自动复制到新 worktree;fixed 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/`; +- 后续编辑、验证、提交、push 和受控 CLI 写操作都在该 worktree 内完成; +- UniDesk 任务 worktree 必须通过 + `bun scripts/cli.ts dev-env worktree add .worktree/ --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/`. 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 菜谱。 diff --git a/docs/reference/g14-observability-infra.md b/docs/reference/g14-observability-infra.md index 156526f6..f540867e 100644 --- a/docs/reference/g14-observability-infra.md +++ b/docs/reference/g14-observability-infra.md @@ -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 diff --git a/docs/reference/g14.md b/docs/reference/g14.md index 57ea592f..909598b9 100644 --- a/docs/reference/g14.md +++ b/docs/reference/g14.md @@ -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`。 diff --git a/docs/reference/hwlab.md b/docs/reference/hwlab.md index 65599eb0..ba78202f 100644 --- a/docs/reference/hwlab.md +++ b/docs/reference/hwlab.md @@ -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/`;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 的压缩上下文继续操作。 diff --git a/docs/reference/pikaoa.md b/docs/reference/pikaoa.md index a9888f20..844c027d 100644 --- a/docs/reference/pikaoa.md +++ b/docs/reference/pikaoa.md @@ -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 测试。 diff --git a/docs/reference/platform-infra.md b/docs/reference/platform-infra.md index a4df1fda..71fb5999 100644 --- a/docs/reference/platform-infra.md +++ b/docs/reference/platform-infra.md @@ -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。 diff --git a/scripts/src/agentrun-manifests.ts b/scripts/src/agentrun-manifests.ts index 4424a1a5..60b4aa0a 100644 --- a/scripts/src/agentrun-manifests.ts +++ b/scripts/src/agentrun-manifests.ts @@ -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'", diff --git a/scripts/src/pac-feature-config-schema-stage.test.ts b/scripts/src/pac-feature-config-schema-stage.test.ts index 5da4e6ba..ff704691 100644 --- a/scripts/src/pac-feature-config-schema-stage.test.ts +++ b/scripts/src/pac-feature-config-schema-stage.test.ts @@ -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", () => { diff --git a/scripts/src/pac-feature-config-schema-stage.ts b/scripts/src/pac-feature-config-schema-stage.ts index 4eb70a3d..cf8bb8cc 100644 --- a/scripts/src/pac-feature-config-schema-stage.ts +++ b/scripts/src/pac-feature-config-schema-stage.ts @@ -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"); +} diff --git a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts index 1a31abba..57b36d3b 100644 --- a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts +++ b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts @@ -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).usage as string[]; diff --git a/scripts/src/platform-infra-pipelines-as-code.ts b/scripts/src/platform-infra-pipelines-as-code.ts index 90e3fa83..d04a0d42 100644 --- a/scripts/src/platform-infra-pipelines-as-code.ts +++ b/scripts/src/platform-infra-pipelines-as-code.ts @@ -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 { 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, key: string, path: string return value; } +function parsePacHttpProbe(obj: Record, 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, 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, key: string, path: string): string { const value = y.stringField(obj, key, path); const parsed = new URL(value);