# Camera **Repository Path**: xiaoqinxing/Camera ## Basic Information - **Project Name**: Camera - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-09-30 - **Last Updated**: 2026-10-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Camera 总仓(umbrella repository) 多仓库 Camera 工程的统一入口:**一个总仓 + 六个独立子仓**(5 个构建子仓 + 1 个用户级 skill 仓 `skills/`),并自动配置好子仓之间的依赖路径,使任何一个子目录都能直接编译, 无需手工 export 一堆环境变量。skill 子仓额外靠一条全局契约软链 `~/.agents/skills` 被 pi / dsh / opencode 自动发现(§3.2)。 ``` Camera/ ├── env.sh ← 路径配置唯一真源(source 一次,全部就位) ├── Makefile ← 统一入口:拉取/体检/编译/并行工作区/锁定版本 ├── repos.tsv ← 子仓清单(本地名 / 目录 / 远端 / 分支 / LFS / 依赖角色) ├── versions.lock ← 生成的版本锁(各子仓的 commit,可提交、可复现) ├── patches/ ← 从旧克隆继承的未提交改动(人工审阅用,非构建输入) ├── scripts/ │ ├── lib.sh 公共函数(清单解析/日志/仓状态) │ ├── bootstrap.sh 克隆/补齐子仓(重仓走本地播种) │ ├── status.sh 子仓分支/领先落后/脏文件总览 │ ├── doctor.sh 环境体检(工具/SDK/工具链/三方源码/路径一致性) │ ├── snapshot.sh 生成 versions.lock │ ├── restore.sh 按 versions.lock 签出 │ ├── adopt-local.sh 从旧 $HOME 克隆继承本地分支与未提交改动 │ ├── check-discovery.sh 依赖自动发现协议回归(env -i 矩阵,§3.1) │ ├── agent-skills.sh 跨 harness skill 仓:真源 + 契约软链(§3.2) │ └── worktree.sh 并行开发工作区(new/list/rm/status) ├── vendor/ ← 子仓:SDK + 交叉工具链(26G,Git LFS) [git ignored] ├── open_source/ ← 子仓:三方开源源码 [git ignored] ├── AF-Algorithm/ ← 子仓:AF 算法 [git ignored] ├── camera-pipeline/ ← 子仓:camera pipeline [git ignored] ├── dataset_pdaf/ ← 子仓:AF 扫峰回归数据包(454M,Git LFS) [git ignored] ├── skills/ ← 子仓:跨 harness agent skill(~/.agents/skills 软链到它)[git ignored] └── worktrees/ ← 并行工作区(worktree.sh 生成) [git ignored] ``` 六个子仓远端: | 子仓 | 远端 | 远端大小 | LFS | |---|---|---|---| | `vendor` | `git@gitcode.com:gcw_PJYf7aqQ/vendor.git` | 26G(.git 5.3G) | ✅ 8025 个文件 | | `open_source` | `git@gitee.com:xiaoqinxing/open_source.git` | 737M | – | | `AF-Algorithm` | `git@gitcode.com:gcw_PJYf7aqQ/AF-Algorithm.git` | 4.7G(.git 457M) | – | | `camera-pipeline` | `git@gitcode.com:gcw_PJYf7aqQ/CameraPipeline.git` | 908M | – | | `dataset_pdaf` | `git@gitcode.com:gcw_PJYf7aqQ/dataset_pdaf.git` | 454M(9 个 tar,全部 LFS) | ✅ `*.tar` / `*.bin` / `*.raw` | | `skills` | `git@gitcode.com:gcw_PJYf7aqQ/skills.git` | ~30M | – | `skills` 是唯一一个**不属于构建依赖**的子仓(用户级:给 pi / dsh / opencode 用的 agent skill), 所以它的 `kind` 写的是 `heavy` —— **不是因为它大**,而是要 worktree 里 symlink 共享主副本 (harness 读的 `~/.agents/skills` 是**全局**契约软链,不能让工作区里再出现一份内容不同的 skills)。 它是六份清单、`versions.lock`、`fetch`/`pull` 的正常成员,只是多一道“建契约软链”的工序(§3.2)。 --- ## 1. 设计取舍:为什么用「清单 + 脚本」而不是 git submodule 总仓**不用 submodule**,子仓保持普通 Git 仓库,由 `repos.tsv` + `bootstrap.sh` 编排。 这不是偏好,而是实测(git 2.53,本机验证)后的结论: | 实测项 | submodule 方案 | 本方案 | |---|---|---| | 子仓内切分支 | 可以,但总仓永远显示 ` M `(脏) | 完全原生,总仓恒干净 | | **总仓开 worktree 后初始化子仓** | ❌ **各自完整重新 clone**(`.git/modules/vendor` 与 `.git/worktrees//modules/vendor` 两份独立对象库,实测无 alternates 共享)→ 每个并行 worktree 对 vendor 多付 5.3G+ | ✅ 轻仓用 `git worktree`;重仓默认 symlink 共享(或 `--clone-heavy` 走硬链接克隆,对象库共享 inode,额外空间≈0) | | 删除含子仓的 worktree | ❌ `git worktree remove` 直接报 `fatal: working trees containing submodules cannot be moved or removed`,只能 `--force` 或手工 `rm -rf` + `prune` | ✅ `git worktree remove` 正常 | | 误跑整体 update | ❌ HEAD 被切回记录版本(detached),未推送提交只能靠 reflog 捞 | 不存在该入口 | | 版本可复现 | ✅ 天然记录 gitlink | ⚠️ 用 `versions.lock` + `make snapshot/restore` 补齐 | | 一条命令拉齐 | ✅ | ✅ `make bootstrap` | 结论:在本项目「多仓并行开发 + 26G 重依赖」的实际形态下,submodule 的空间与 worktree 代价高于它带来的版本记录收益,因此把可复现性交给 `versions.lock`。 --- ## 2. 快速开始 ```bash cd ~/Camera make doctor # 环境体检(先看缺什么) make bootstrap # 克隆/补齐六个子仓(重仓走本地播种,见 §6)+ 建 skill 契约软链(§3.2) make status # 子仓分支/状态总览 make agent-skills # 配置/查看跨 harness skill 仓(真源 skills/ + 契约软链 ~/.agents/skills,§3.2) make build-af # 本机编译 AF-Algorithm(preset host-release) make build-pipeline # 本机编译 camera-pipeline(build/release) make test-af # AF 测试(ctest) make test-pipeline # pipeline 测试(ctest) make board SOC=hi3519dv500 # 板端交叉编译(走 camera-pipeline/scripts/build-board.sh) make cross-af # AF 交叉编译(强制用本总仓 vendor 工具链) make verify # 一键:状态 + 体检 + 两仓编译 + 刷新 versions.lock ``` 交互式开发时只需要一次 `source`: ```bash cd ~/Camera source env.sh -v # 打印一份路径摘要 cd AF-Algorithm && cmake --preset host-release && cmake --build --preset host-release -j ``` --- ## 3. 路径如何自动配置 分两层:**子项目自查(默认,2026-09 起,见 §3.1)** + **总仓 `env.sh` 显式覆盖**。 核心:`env.sh` **以自身所在目录推导根目录**(`dirname "${BASH_SOURCE[0]}"`), 绝不写死 `$HOME`。带来的直接好处: * 总仓改名/搬移后依然有效; * `worktrees//` 里的 `env.sh` 指向的是**该工作区自己**的依赖目录,不会串到主工作区; * 子项目源码**零改动** —— 它们本来就读环境变量。 导出的变量与消费方: | 变量 | 值 | 谁在用 | |---|---|---| | `CPPIPE_VENDOR_ROOT` | `$CAMERA_ROOT/vendor` | camera-pipeline `CMakeLists.txt` / `scripts/build-board.sh` | | `CPPIPE_OPEN_SOURCE_ROOT` | `$CAMERA_ROOT/open_source` | 同上(jsoncpp/x264/opencv 源码) | | `AF_VENDOR_ROOT` / `AF_OPEN_SOURCE_ROOT` | 同上两者 | AF-Algorithm `CMakeLists.txt`、`cmake/Camera*.cmake` | | `JSON_DIR` / `GTEST_DIR` | `open_source/jsoncpp` / `open_source/googletest` | 两个子仓的单库覆盖 | | `CAMERA_DATASET_PDAF_DIR` / `AF_SWEEP_REGRESSION_SHARED_ROOT` | `dataset_pdaf` / `dataset_pdaf/datasets` | AF-Algorithm `tools/scripts/sweep-regression.sh`、`tools/simulation/scripts/sweep_regression.py`(扫峰回归数据包;已存在的同名环境变量优先,可临时换目录) | | `HI3519DV500_SDK_DIR` / `SS928V100_SDK_DIR` | `vendor//` | `vendor/toolchains/hisi-*.cmake`(由父/祖父目录推导工具链) | | `HI3519DV500_TOOLCHAIN_DIR` / `SS928V100_TOOLCHAIN_DIR` | `vendor/toolchains/<工具链>` | 同上(优先级最高) | | `SOC` / `SDK_DIR` / `TC_DIR` | 由 `CAMERA_SOC` 派生;**`SDK_DIR` 语义是 `/smp/a55_linux`**(与 `build-board.sh` 一致,与 `*_SDK_DIR` 不同) | camera-pipeline `scripts/build-board.sh` | | `CAMERA_TOOLCHAIN_FILE` | `vendor/toolchains/hisi-.cmake` | `make cross-af`(显式覆盖;子仓自查已能自行定位工具链,见 §3.1) | 开关:`CAMERA_SOC=hi3519dv500|ss928v100`(默认前者)、`CAMERA_ADD_TOOLCHAIN_PATH=1` (把交叉工具链 bin 前置到 PATH,默认关,避免遮蔽宿主工具)。 自检: ```bash make check-paths # 打印摘要 + 逐项存在性 make doctor # 上述 + 宿主工具 + SDK/工具链关键文件 + 路径一致性告警 make check-deps # 干净环境(env -i)下验证依赖自动发现协议(见 §3.1) ``` ### 3.1 依赖自动发现协议(子项目自查,不依赖任何注入) 2026-09 起,两个子仓会**自己找依赖**,因此在任何位置新建的 worktree 都不需要 `source env.sh`,也不需要 `$HOME` 软链或任何新命令: * 实现:`AF-Algorithm/cmake/CameraDeps.cmake`、`camera-pipeline/cmake/CameraDeps.cmake` (同一份逻辑,各仓自包含)+ `camera-pipeline/scripts/deps-root.sh`(shell 侧,供 `build-board.sh` / `fetch_third_party.sh` 共用)。 * **优先级**:环境变量 / `-D` 显式指定 > **总仓自查** > `$ENV{HOME}` 默认。 * **总仓标记**:某目录同时含 `repos.tsv` 与 `env.sh`。 * **查找路径**: 1. 从源码目录逐级向上; 2. 未命中时用 `git rev-parse --git-common-dir` 反查主克隆(worktree 会指向主克隆的 `.git`),再从主克隆所在目录逐级向上 —— 这一步覆盖「建在总仓树外的 worktree」。 * **语义:依赖跟着主克隆走**。从总仓克隆出来的 worktree → 总仓依赖;从别处克隆的仓及其 worktree → `$HOME` 依赖(行为与改动前一致,不会被总仓“劫持”)。 * 每次 configure 都打印来源,例如: ``` -- camera-deps: AF_VENDOR_ROOT=/home/liqinxing/Camera/vendor [来源: 总仓自查 /home/liqinxing/Camera] ``` 若沿用了既有 build 目录的旧 cache 值,会打印「注意:沿用既有 cache 值…」并给出 `-U<变量>` 重探方法(避免静默用错路径)。 * 回归验证:`make check-deps`(7 个用例:总仓主克隆 ×2、树外 worktree ×2、显式 `-D` 覆盖、 总仓外独立仓 ×2)。 `env.sh` 因此从“必需注入”降级为**显式覆盖层**:CI、非常规布局、切 `CAMERA_SOC` 时仍可用, 日常开发不再需要。子仓改动未推送前,旧检出仍走 `$HOME` 回落(见 §7.0)。 ### 3.2 跨 harness 共享 skill 子仓(`skills/` + 全局契约软链 `~/.agents/skills`) 本总仓同时负责把**给 coding agent 用的 skill** 配到机器上。`skills` 是第六个子仓 (`repos.tsv`,`kind=heavy`),它比其它子仓多一道工序:一条**全局契约软链**。 ```text ~/Camera/skills/ ← 子仓(真源;gitcode 仓 clone,已被 .gitignore 忽略) ~/.agents/skills -> ~/Camera/skills ← 契约路径(软链):pi / dsh / opencode 写死的扫描路径 ``` 自动化分两层(都是幂等的): | 步骤 | 由谁负责 | |---|---| | ①真源 checkout(克隆/拉取/版本锁定/并行工作区) | `repos.tsv` + `make bootstrap` / `pull` / `snapshot` / `wt-new`(普通子仓待遇) | | ②全局契约软链 `~/.agents/skills` → 真源 | `scripts/agent-skills.sh`(`make agent-skills`;`make bootstrap` 末尾自动跑一次) | 为什么②就够了:**pi、DeepSeek harness(dsh)、opencode 都原生自动扫描 `~/.agents/skills`**, 不需要给每个 harness 另写配置: | Harness | 自动发现依据 | |---|---| | `pi` | `~/.agents/skills` 作为 user 级 skill 源自动收集(项目内 `.agents/skills` 在项目受信任后收集) | | `dsh` 0.2.0-rc.2 | `dsh-skill-filesystem` 默认根,优先级 `/.dsh/skills`(100) → `/.agents/skills`(200) → `customSkillDirs`(300) → `~/.dsh/skills`(400) → **`~/.agents/skills`(500)** | | `opencode` | 外部 skill 自动加载:`~/.claude/skills`、`~/.agents/skills` | 路径由 `env.sh` 提供(`CAMERA_AGENT_SKILLS_REPO` 子仓目录 / `_DIR` 契约路径 / `_URL` / `_BRANCH`), 均可环境变量覆盖;`make status` 会像其它子仓一样列出它(额外一行标出契约软链)、`make doctor` 体检。 **worktree 语义**:`kind=heavy` ⇒ `make wt-new` 在工作区里把它做成 `skills -> ../../skills` 的 symlink(与 vendor 等重仓一致),所以“harness 读的那份”与“你在改的那份”永远是同一个目录。 `env.sh` 还会用 `git rev-parse --git-common-dir` 把真源定位到**主克隆**,单独跑 `make agent-skills` 也不会在 worktree 里克隆/迁移出第二份。 **为什么真源不放在 `<总仓>/.agents/skills`**(实测否决,避免以后重复讨论): | | `~/Camera/skills`(采用) | `~/Camera/.agents/skills`(否决) | |---|---|---| | pi | 无影响 | `.agents/skills` 是 pi 的**项目级** skill 目录,存在即使项目被判定“需要信任”(`trust-manager` 逐级向上找)→ 每个会话弹信任/项目资源被门控 | | dsh | 1 条记录(rank 500) | 同名 **3 条**(rank 200 项目 + 400 + 500),告警也出现两次;“项目级覆盖全局”语义变含糊 | | opencode | 1 条(home 路径) | 不重复、无信任门槛,但上报 location 随 cwd 漂移(进 `AF-Algorithm` 子仓又回落 home) | 功能上两种都不缺 skill(契约软链都在),差别只在副作用 —— 所以选副作用最小的 `skills/`。 真需要“项目专用 skill”时,应该在 `~/Camera/.agents/skills/` 放另一套,而不是这个全局共享仓。 **迁移语义**:若契约路径现在是实体 git 仓、而真源目录还不存在(老布局),脚本会校验 origin 一致后 `mv` 到真源位置再建软链(只搬目录,不删数据);origin 不一致只告警、不自动搬。 ```bash make agent-skills # 确保真源 + 契约软链(缺失才克隆/迁移)+ 打印状态 make agent-skills ARGS=--update # 额外 git pull --ff-only(脏工作区/分叉会安全退出) make agent-skills ARGS=--relink # 契约路径指向别处时重指到真源 make agent-skills ARGS=--dsh-link # 额外维护 ~/.dsh/skills/ 软链(DSH rank 400 优先路径) make agent-skills-status # 只读查看 CAMERA_SKIP_AGENT_SKILLS=1 make bootstrap # 完全不处理(CI/无权限机器) ``` 失败语义:`bootstrap` 里这一步**失败不阻塞**(提示后可继续),单独跑 `make agent-skills` 时严格失败(便于脚本化)。克隆走 SSH,用 `BatchMode=yes` 以免卡在交互式询问;无 key 时给 出换 `CAMERA_AGENT_SKILLS_URL` / 换路径 / 跳过三种办法。 已知代价:真源在总仓树内 ⇒ 删掉/搬走 `~/Camera` 会让契约软链悬空,三边 harness **静默地** 看不到任何 skill(自检:`ls -l ~/.agents/skills`;`make agent-skills-status` 会列出当前有哪些 skill)。 **新增 skill 的规范**(三边都按 Agent Skills 标准解析,写错会“静默不加载”): ```text skills//SKILL.md # 目录名 = frontmatter 的 name;文件名必须是大写 SKILL.md --- name: my-skill # 必需:小写字母/数字/连字符,≤64,与目录名一致 description: 做什么 + 何时触发(写具体关键词,≤1024) # 必需;缺了 harness 直接不加载 --- ``` - **不要**给仓内普通文档(README、`references/*.md`)加 `name` + `description` frontmatter: 在 `~/.agents/skills` 这种共享根里,pi 会把分组目录下的嵌套 `.md` 当 skill 加载,DSH 会把根目录 顶层 `*.md` 当扁平 skill —— 会被误注册成一个 skill。 - DSH 只扫扫描根**顶层**:`//SKILL.md` 或 `/.md`;嵌套 `**/SKILL.md` 不发现。 DSH 的 `~/.dsh/skills`(rank 400) 优先于 `~/.agents/skills`(500),同名会取 400 那份。 - 关掉自动发现的开关(本机均未设置):opencode 的 `OPENCODE_DISABLE_EXTERNAL_SKILLS=1` / `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1`;pi 的 `--no-skills`。 - 加完 skill 后:`cd ~/Camera/skills && git add && git commit && git push`, 其他机器用 `make agent-skills ARGS=--update` 快进;无需重启任何 harness 的配置(新会话/重启进程即可)。 --- ## 4. Makefile 目标 | 目标 | 说明 | |---|---| | `bootstrap` / `bootstrap-fresh` | 补齐子仓(重仓本地播种)/ 全部从远端全新克隆;同时配置跨 harness skill 仓(§3.2) | | `agent-skills` / `agent-skills-status` | 配置 / 查看跨 harness skill 仓(真源 `skills/` + 契约软链 `~/.agents/skills`,§3.2;`ARGS='--update --relink --dsh-link'`) | | `status` / `doctor` / `env` / `check-paths` | 状态、体检、路径摘要、路径存在性 | | `check-deps` | 干净环境下验证依赖自动发现协议(§3.1,默认含总仓外独立仓用例) | | `fix-vendor` | 清理 vendor 里由 toolchain 文件写入的无前缀 `as`/`ld` 软链接(§7.3) | | `shell` | 进入已 source `env.sh` 的交互式 shell | | `build-af` / `test-af` | AF-Algorithm 本机构建 / ctest(`AF_PRESET` 可选) | | `build-pipeline` / `test-pipeline` | camera-pipeline 本机构建 / ctest(`PIPE_BUILDDIR` 可选) | | `cross-af` / `board` | AF 交叉编译 / camera-pipeline 板端交叉编译(`SOC=`) | | `build-all` / `verify` | 两仓都构建 / 端到端冒烟 + 刷新锁文件 | | `snapshot`(= `lock`) / `restore` | 生成 / 应用 `versions.lock` | | `fetch` / `pull` | 所有子仓 fetch / 快进拉取 | | `adopt-local` | 从旧 `$HOME` 克隆继承本地分支与未提交改动 | | `wt-new` / `wt-list` / `wt-rm` / `wt-status` | 并行工作区 | | `clean-af` / `clean-pipeline` / `clean` | 清理子仓构建产物 | 可覆盖变量:`JOBS`(默认 `nproc`)、`AF_PRESET`、`AF_CROSS_PRESET`、`PIPE_BUILDDIR`、 `CAMERA_SOC`、`ARGS`(透传给脚本,如 `make board ARGS=probe`)。 --- ## 5. worktree 并行开发 ```bash make wt-new NAME=feat1 # 建工作区:worktrees/feat1 # 轻仓(AF-Algorithm / camera-pipeline) → 各自 git worktree,分支 wt/feat1 # 重仓(vendor / open_source / dataset_pdaf) → symlink 共享主工作区副本 make wt-new NAME=feat2 ARGS=--clone-heavy # 重仓也要独立副本(硬链接对象库 + LFS 播种) cd worktrees/feat1 source env.sh # 自动指向本工作区的依赖 make build-af build-pipeline make wt-list # 所有工作区 + 子仓状态 make wt-status NAME=feat1 make wt-rm NAME=feat1 ARGS='--force --delete-branch' ``` 注意事项: * 轻仓在两个工作区里**不能同时签出同一分支**(Git 限制);`wt-new` 默认建 `wt/` 分支。 * 若同一时刻要在多个工作区里改 vendor/open_source 本身,用 `--clone-heavy`; 硬链接克隆共享对象库 inode,删除主工作区不影响已克隆(对象有独立目录项)。 * `make wt-rm` 会先摘子仓 worktree 与 symlink,再删总仓 worktree —— 顺序是必须的。 ### 5.1 子仓单独开 worktree(不经总仓)时的路径 **`env.sh` 属于总仓 Camera,不会被子仓的 worktree “继承”**(实测: `git -C AF-Algorithm ls-files | grep env.sh` = 0,该目录里根本没有 env.sh)。 但**现在也不再需要它**:子仓会自查依赖(§3.1),所以熟悉的命令照旧就能用: ```bash git -C ~/Camera/AF-Algorithm worktree add ~/AF-wt/feat1 -b feat1 cd ~/AF-wt/feat1 && cmake --preset host-release && cmake --build --preset host-release -j # configure 会打印:camera-deps: AF_VENDOR_ROOT=~/Camera/vendor [来源: 总仓自查 ~/Camera] ``` 各位置的解析结果(即 `make check-deps` 验证的矩阵): | 位置 | 依赖指向 | 说明 | |---|---|---| | 总仓内主克隆 / 总仓 `make wt-new` 工作区 | 总仓 `vendor` / `open_source` | 向上找标记即命中 | | **建在总仓树外的 worktree**(如 `~/AF-wt/feat1`) | 总仓 `vendor` / `open_source` | 靠 `git-common-dir` 反查主克隆 | | 总仓外的独立克隆及其 worktree(如 `~/AF-Algorithm`、`~/AF-Algorithm-worktrees/*`) | `$HOME/vendor` / `$HOME/open_source` | 行为不变,不被总仓劫持 | | 任何位置 + `-D` / 环境变量 | 显式指定值 | 优先级最高(CI / 非常规布局用) | 两个坑(仍然适用): 1. **不要把 `env.sh` 复制/软链进子仓 worktree**:它按自身位置推导根目录,会算成 `CAMERA_ROOT=<该 worktree>` → `CPPIPE_VENDOR_ROOT=<该 worktree>/vendor`(不存在)。 2. **子仓改动未推送前**,其他机器 / 旧检出仍走 `$HOME` 回落:这时需要 `source ~/Camera/env.sh` 或显式 `-D`(§7.0)。 依赖共享(只读)是安全的;产物落在各自 worktree 的 `build/` 与 `output/-<类型>/`,互不覆盖。 (注:AF 的 `compile_commands.json` 软链逻辑只在构建目录已存在该文件时回写源码目录, 因此用 `-B /tmp/...` 做一次性验证时请用**全新**构建目录。) --- ## 6. 版本锁定与与旧克隆的关系 ```bash make snapshot # 写 versions.lock(name/dir/commit/branch/state/url) make restore # 按锁文件签出(detached HEAD;--force 覆盖脏文件;--print 只打印) ``` `state=dirty` 说明生成锁文件时该子仓有未提交改动:**只有 commit 可复现,工作区改动不可**。 旧克隆(`~/vendor`、`~/open_source`、`~/AF-Algorithm`、`~/cameraPipeline`、`~/dataset_pdaf`)**保持原位未动**, 总仓内是独立的全新克隆。两边的差异: * **vendor 用本地播种克隆**(`bootstrap.sh` 默认 `--vendor-mode seed`): 从 `$HOME/vendor` 硬链接对象库(省 5.3G 网络与磁盘)并硬链接播种 LFS 缓存 (`~/.local/bin/git-lfs`,8025 个 LFS 文件,副本 fsck 通过),再从真实远端 `fetch` 同步 refs。克隆结果是**独立仓库**(对象由 hardlink 持有,旧仓删除也不受影响)。 * **其他重仓同样本地播种**(`bootstrap.sh` 默认,`kind=heavy` 且播种源远端匹配时自动启用): `dataset_pdaf`(454M,9 个 `*.tar` 走 LFS)从 `$HOME/dataset_pdaf` 硬链接对象库与 LFS 缓存 (省 434M 下载),再 fetch 真实远端同步 refs;实测 21s 完成、`lfs fsck --pointers` 通过。 播种源不存在或远端不符时自动退回网络克隆;换根用 `CAMERA_SEED_ROOT=/path`, 关闭用 `CAMERA_SEED_ROOT=`(vendor 仍由 `--vendor-mode` 控制)。 * 旧克隆里的**本地分支**已导入为 `old/*` 远端跟踪引用(AF-Algorithm 70 个、camera-pipeline 7 个…): ```bash git -C AF-Algorithm log --oneline old/ # 查看 git -C AF-Algorithm branch <名字> old/ # 落到本地分支 ``` * 旧克隆里的**未提交改动**已导出供人工审阅,**不会自动应用**(旧仓也未动): * `patches/.patch` —— `git diff HEAD`(已暂存 + 未暂存,含已暂存的新文件); 实测三份补丁都 `git apply --check` 通过,可在总仓克隆里 `git -C apply ../patches/.patch`。 * `patches/.untracked` —— 未跟踪文件清单(仅列名,不收录内容,如旧仓的 `*.o` 构建残留)。 * 重新导入:`make adopt-local ARGS='--patches'`。 * 注:`camera-pipeline.patch` 里的改动(HTTPS/WSS 预览等)已于 2026-09-29 由上游 `c6574de` / `38be214` 提交,现在仅作对照;`vendor.patch`(toolchain 回退)仍然未上游化。 --- ## 7. 已知的不一致与待办 状态标记:🔧 **已在子仓工作区实现(未提交推送)**、⬜ 待处理、✅ 已在总仓层解决。 0. ✅ **依赖自动发现已落地并推送**(协议见 §3.1): * `AF-Algorithm` `d4c3f39b`:`cmake/CameraDeps.cmake`(新)、`CMakeLists.txt`、 `CMakePresets.json`、`platform/hisi_3519dv500/CMakeLists.txt` * `camera-pipeline` `bf9881a`:`cmake/CameraDeps.cmake`(新)、`scripts/deps-root.sh`(新)、 `CMakeLists.txt`、`scripts/build-board.sh`、`scripts/fetch_third_party.sh` 其他机器 / 旧检出 `make pull` 后即生效;两仓各存一份同协议实现(CMake 与 shell 各一套), **改一处必须同步另一仓**并跑 `make check-deps`。 1. ⬜ **`camera-pipeline/scripts/build-board.sh` 板端链接缺少 `soft.o`(当前板端构建必失败)** 该脚本的 `CORE` 编译了 `src/hal/{sns,sns_dynamic,mock|mpp}.cpp`,但**从不编译 `src/hal/soft.cpp`**,而板端 HAL 的 `create_osd()` 会 `new soft_osd()` → 链接报 `undefined reference to 'vtable for soft_osd'`。 * 已对比 `3544eed` 与 `5f27e63` 两个 commit:`CORE` 行完全一致 → **不是新提交引入的回归,是长期存在的脚本缺陷**。 * 已在总仓侧用补丁副本验证修法(不改子仓):编译 `src/hal/soft.cpp -o build/arm/soft.o` 并把 `${BUILD_DIR}/soft.o` 加进 `CORE` → `bench/example_hello` 与 `probe`(MPP 真机 HAL) 均构建成功,产物 `ELF 64-bit ARM aarch64, statically linked`。 2. ✅ **AF-Algorithm 交叉 preset 曾硬编码 `$env{HOME}/vendor/toolchains/*.cmake`**(已修,`d4c3f39b`) → 现为:preset 不再设 `CMAKE_TOOLCHAIN_FILE`,由 `CMakeLists.txt` 在 `project()` **之前** 按自查结果设置。实测干净环境(`env -i`)下 `cmake --preset cross-hisi-3519dv500-*` 自动命中 `Camera/vendor/toolchains/hisi-3519dv500.cmake`,且派生的 `AF_HISI_3519DV500_SDK_ROOT`(原本也硬编码 `$ENV{HOME}`)同时指向总仓 vendor。 副证:AF 的 pre-commit 门禁(无任何环境变量)跑通了 3519D Release 交叉全量构建。 3. ⬜ **`vendor/toolchains/hisi-3519dv500.cmake`(committed 版)会往工具链 bin 目录写无前缀 `as`/`ld` 软链接** —— 实测确认:**任何一次用该 toolchain 文件的交叉配置**(`make cross-af`、 `cmake --preset cross-hisi-3519dv500-*`)都会在 `Camera/vendor` 写入这两个未跟踪软链接, 有让宿主 x86_64 编译命中 aarch64 `ld`(`unrecognised emulation mode: elf_x86_64`)的风险。 总仓提供 `make fix-vendor` 一键清理(`make doctor` 会提示)。 旧克隆 `~/vendor` 里有一份**本地回退**(已导出为 `patches/vendor.patch`),总仓克隆里没有。 **建议把该回退提交到 vendor 仓**,而不是各自留本地改动。 4. ✅ **SS928V100 板端/交叉路径已打通(未上板)**:包内没有 `source/out`(解包后是 `smp/a55_linux/{mpp,osal,vendor,...}`,MPP 产物在 `mpp/out`)。两个子仓已按该布局适配: - camera-pipeline:`SOC=ss928v100 ./scripts/build-board.sh {lib,probe,test_hw}` 全通 (工具链 `aarch64-mix210-linux`、库清单、SDK 头文件切分、VB/FMU/3DNR/AIPP 平台分支; 产物目录 `build/arm-ss928v100/`);主机可 `cmake -B build/ss928-host -DCPPIPE_SOC=ss928v100` 跑全量测试与 mock 端到端(GC50E PPRAW/PDAF 链路两平台同源); - AF-Algorithm:`cmake --preset cross-hisi-ss928v100-cxx11-prod` 全量构建通过 (仅 C++11 生产链路:GCC 7.3 无 ``/``,测试/仿真/af-web-console 需 C++17)。 **上板仍待做**:GC50E 的 bayer/OB/I2C 核对、ISP 旁路位与统计掩码语义、NPU 模型按 SS928 ATC 重转、 af-web-console 部署脚本参数化(详见子仓 `docs/implementation/hisi-ss928v100-port.md` 与 camera-pipeline README 的 PPRAW 小节)。`make doctor SOC=ss928v100` 仍按实际结构检查并提示。 5. ✅ **变量语义坑**:`SDK_DIR`(`build-board.sh`,= `/smp/a55_linux`)与 `HI3519DV500_SDK_DIR`(vendor toolchain cmake,= SDK 顶层)含义不同;总仓已在 `env.sh` 中 分别提供并注释。AF-Algorithm 不使用 `SDK_DIR`。 6. ⬜ **AF 的 `cxx11-prod` preset 当前不能编译**:`tools/board_calib/PdOffsetCalib.cpp:911` `rois.push_back(Roi{x0, y0, x1 - x0, y1 - y0});` —— 该类型 C++11 下非聚合 → `no matching function for call to '{anonymous}::Roi::Roi()'`。 已用 `compile_commands.json` 里的原始命令隔离验证:`-std=c++11` 报错、`-std=c++17` 通过; 与依赖路径改动无关(rebase 到上游最新后仍然复现)。 注:AF 的 pre-commit 「C++11 生产链路编译门禁」不覆盖该工具,所以门禁能过而 preset 不能编译; C++17 的 `cross-hisi-3519dv500-release` 不受影响(已全量构建通过)。 7. ⬜ **AF 工具脚本仍硬编码 `$HOME`**(非构建路径,本次未处理): `tools/scripts/af-web-console-deploy.sh`(`${AF_ONNXRUNTIME_ROOT:-$HOME/vendor/onnxruntime}`)、 `tools/scripts/convert_*.sh`(`$HOME/SDK/hi3519dv500_npu_env.sh`、`$HOME/HumanDetection`)。 8. ✅ **AF 交叉构建的选项**(`AF_ENABLE_TESTS` 等)仍由 preset 决定;总仓只覆盖依赖与工具链路径。 9. ✅ **`dataset_pdaf` 的 LFS 与 AF 文档已对齐(AF `0e8f9249`)**:数据集仓用 Git LFS 跟踪 `*.tar` / `*.bin` / `*.raw`(subrepo commits `683396d`、`148819c`),AF 的 `tools/simulation/sweep-regression/README.md` 原先写「仓库未启用 Git LFS」已更正为 `git clone` + `git lfs pull`(或总仓 `make bootstrap`),并保留「tar 严禁提交到 AF 仓」; 安装体积按实测从 350 MB 改为约 454 MB(LFS 434 MB)。另外 `git status` 需要 `git-lfs` 在 PATH (`env.sh` 已代为前置 `~/.local/bin`,`status/snapshot/restore/adopt-local` 已 source 它)。 10. ✅ **AF 数据根解析统一为依赖自查(总仓优先)**(AF `0e8f9249` + `465c71ed`):原先固定 `$HOME/dataset_pdaf/datasets`,AF 在总仓内时仍读家目录旧副本(实测提交门禁日志全是 `/home//dataset_pdaf/...`)。现优先级为 `AF_SWEEP_REGRESSION_SHARED_ROOT` > 总仓 `dataset_pdaf/datasets` > `$HOME/dataset_pdaf/datasets`(与 `cmake/CameraDeps.cmake` 同协议,含树外 worktree 用 `git-common-dir` 反查主克隆);总仓存在但未 bootstrap 数据集时 仍回落 `$HOME`。消费方:`tools/scripts/sweep-regression.sh`(提交门禁链路)、 `tools/simulation/scripts/sweep_regression.py`、`tools/scripts/build_head_detector_calib.py` (`--scene-root`)。本仓 `env.sh` 的 `AF_SWEEP_REGRESSION_SHARED_ROOT` 仍作为显式覆盖层生效。 该协议现有**五处**实现(改一处必须同步其余):`AF-Algorithm/cmake/CameraDeps.cmake`、 `AF-Algorithm/tools/scripts/deps-root.sh`、`AF-Algorithm/tools/scripts/deps_root.py`(扫峰回归 与校准集共用的 Python 实现)、`camera-pipeline/cmake/CameraDeps.cmake`、 `camera-pipeline/scripts/deps-root.sh`。 11. ⬜ **`scripts/worktree.sh new` 复用残留 `wt/` 分支时不会提示“落后多少”**(2026-10 冒烟时发现, 与 skill 仓改动无关):上次 `wt-rm NAME=x` 未加 `--delete-branch` 会留下 `wt/x` 分支, 再次 `wt-new NAME=x` 会命中「分支已存在」分支 → 复用旧分支建 worktree,工作区落在旧提交 (实测落后 master **19 个提交**),只有一行 `已存在(残留),改为在该分支上创建 worktree` 警告。 * 当前规避:`make wt-rm NAME=x ARGS='--force --delete-branch'`,或先 `git branch -D wt/x`。 * 建议修法(未做):复用分支时打印 `git -C "$CAMERA_ROOT" rev-list --count ..HEAD`, 或对复用分支给出改用 `--base HEAD` 的提示。 --- ## 8. 排错 | 现象 | 处理 | |---|---| | `git: 'lfs' is not a git command` | 装 git-lfs 到 `~/.local/bin`(`env.sh` 会自动前置该目录到 PATH) | | vendor 工作区出现 130 字节的 `version https://git-lfs...` 文本文件 | LFS 未 smudge:`cd vendor && git lfs pull`,或按 §6 重新播种 | | dataset_pdaf 里的 `*.tar` 只有 130 字节(LFS 指针没 smudge) | `cd dataset_pdaf && git lfs pull`;或删掉目录后 `scripts/bootstrap.sh --only dataset_pdaf` | | `make status` / `make doctor` 报 `git-lfs: not found` | git-lfs 不在 PATH(常见于 `~/.local/bin`):`env.sh` 会自动前置;`status/snapshot/restore/adopt-local` 已自行 `source env.sh` | | AF 扫峰回归报 `[MISSING] 数据包未安装` | 数据集未安装或路径不符:`make bootstrap`(或 `git -C dataset_pdaf lfs pull`),然后 `make env` 确认 `AF_SWEEP_REGRESSION_SHARED_ROOT` | | `module 'jsoncpp' not found` / AF 找不到 gtest | `source env.sh` 后构建,或确认 `open_source/{jsoncpp,googletest}` 存在 | | camera-pipeline 报 SDK 路径无效 | `make check-paths`;确认 `CAMERA_SOC` 与实际 SDK 目录一致 | | `make board` 报找不到交叉编译器 | 确认 `vendor/toolchains/<工具链>/bin/*-g++` 存在;必要时 `CAMERA_ADD_TOOLCHAIN_PATH=1` | | 子仓跟不上远端 | `make fetch` 看差异,`make pull` 快进(脏工作区会跳过并提示) | | 想回到某个已验证组合 | `make restore`(配合已提交的 `versions.lock`) | | 子仓 worktree 里 `source env.sh` 报找不到文件 | 正常且**不需要**:`env.sh` 只在总仓(§5.1),依赖由子仓自查(§3.1) | | 子仓 worktree 编译用了旧克隆依赖(`/home/.../open_source`) | 该 worktree 的主克隆在总仓外 —— 设计语义“依赖跟主克隆走”(§3.1)。要强制用总仓依赖:`source env.sh` 或加 `-D` | | configure 打印「注意:沿用既有 cache 值」 | 该构建目录是旧逻辑配过的:按提示 `cmake -B -U<变量>` 重探,或删掉构建目录重配 | | 子仓已改但另一台机器仍用 `$HOME` 依赖 | 依赖自查改动尚未推送:先在子仓提交推送,或临时 `source env.sh` / 用 `-D`(现已推送,`make pull` 即可) | | `make status` 里 vendor 突然 DIRTY=2 | 交叉配置时 vendor 的 toolchain 文件写了 `bin/as`、`bin/ld` 软链接:`make fix-vendor` 清理(§7.3) | | `bootstrap` 报 skill 子仓克隆失败 | 克隆走 gitcode SSH:`ssh -T git@gitcode.com` 验证;或换远端 / 换子仓路径 / 跳过(三种办法见 §3.2)。这一步失败**不阻塞**其余子仓 | | `~/.agents/skills` 软链悬空(agent 看不到 skill) | 子仓被删/被搬:`make bootstrap`(克隆子仓)或 `make agent-skills`(修链);Camera 搬过位置时用 `ARGS=--relink` 重指 | | `~/.agents/skills` 是实体目录或指向别处 | `make agent-skills` 会:老布局实体仓→自动迁移到真源位置;指向别处或非仓→告警不覆盖(`ARGS=--relink` 可强制重指) | | agent 侧看不到新 skill | 三边都是启动时扫描:pi/opencode 需**新会话**、dsh 重启。定位:`make agent-skills-status`(列出仓内 `/SKILL.md`)、`opencode debug skill`(看 `location` 是否指向 `~/.agents/skills/...`)、`ls -l ~/.agents/skills`(软链是否悬空) | --- ## 9. 本次搭建的验证记录 | 验证项 | 命令 | 结果 | |---|---|---| | 环境体检 | `make doctor` | 全部通过(git / cmake 4.2.3 / g++ / ccache / git-lfs 3.5.1、四子仓、8 项依赖路径、jsoncpp+googletest+cmake-modules、SDK include+lib、musl 交叉 g++) | | vendor LFS 完整性 | `git -C vendor lfs fsck --pointers` | `Git LFS fsck OK`(8025 个 LFS 文件无残留指针,播种自 `~/vendor` 的本地缓存) | | AF-Algorithm 本机构建 | `make build-af` | 100% 编译通过(preset `host-release`) | | AF-Algorithm 测试 | `make test-af` | **1385/1385 通过**(2 个按设计 skip),8.4s | | camera-pipeline 本机构建 | `make build-pipeline` | 100% 编译通过(`build/release`) | | camera-pipeline 测试 | `make test-pipeline` | **18/18 通过**,73s | | 板端交叉编译(SDK 路径自动配置) | `make board` | 交叉编译 jsoncpp+核心成功,证明 SDK/工具链路径自动配置生效;链接阶段因 §7.1 的子仓脚本缺陷失败(非总仓问题,修法已验证) | | 板端 probe(MPP 真机 HAL) | 补丁副本 `build-board.sh probe` | 成功,产物 `ELF 64-bit ARM aarch64` | | AF 交叉编译(工具链路径覆盖) | `make cross-af` | 成功,产物 `ARM aarch64` | | worktree 工作区 | `make wt-new NAME=smoke` → 工作区内构建 → `make wt-rm NAME=smoke` | 创建正常、独立分支 `wt/smoke`、工作区内 `cmake` 解析到工作区自己的依赖(`worktrees/smoke/vendor`)、**`worktree remove` 无需 `--force` 即干净删除** | | 版本锁 | `make snapshot` / `make restore ARGS=--print` | 生成与解析正确(四仓 commit/分支/clean 状态) | | bootstrap 幂等性 | `make bootstrap`(二次执行) | 四仓全部「已存在,跳过」 | | 脚本语法 | `bash -n env.sh scripts/*.sh` | 全部通过 | | 旧克隆继承 | `make adopt-local ARGS=--patches` | AF 70 个 / camera-pipeline 7 个 / vendor、open_source 各 1 个本地分支导入为 `old/*`;未提交改动导出到 `patches/*.patch`(旧仓未被改动) | | **依赖自动发现协议** | `make check-deps`(7 个用例,均 `env -i` 干净环境) | 全部通过:总仓主克隆 ×2 / 总仓树外 worktree ×2 → 总仓依赖;总仓外独立仓 ×2 → `$HOME` 依赖(不被劫持);显式 `-D` 覆盖生效 | | **干净环境端到端交叉构建** | 树外 worktree 里 `env -i … bash scripts/build-board.sh bench example_hello` | 成功,产物 `ELF 64-bit ARM aarch64`(无环境变量、无软链、未 source 任何东西) | | **干净环境交叉 preset** | 树外 worktree 里 `env -i … cmake --preset cross-hisi-3519dv500-cxx11-prod` + 全量构建 | 工具链自动命中总仓 `vendor/toolchains/hisi-3519dv500.cmake`,SDK 根亦派生到总仓 vendor;除 §7.6 的 `pd_offset_calib`(C++11)外全部构建通过 | | **依赖改动后的总仓回归** | `make build-af test-af build-pipeline test-pipeline cross-af` | AF 构建 rc=0 / 测试 **1385/1385**;pipeline 构建 rc=0 / 测试 **18/18**;`cross-af` 全量 rc=0;`make board` 仍为 §7.1 既有 soft.o 缺陷(无新增失败) | | **推送前 rebase 上游后重验证**(上游各多 2 个提交;无冲突) | 同上 + `make check-deps` / `make doctor` | AF 构建 rc=0 / 测试 **1388/1388**(含上游新增 control 测试);pipeline 构建 rc=0(TLS 启用 OpenSSL 3.5.5)/ 测试 **18/18**;`cross-af` rc=0;`check-deps` 7/7;`doctor` 通过;已确认上游 OpenSSL 改动与本仓自查改动在 `CMakeLists.txt`/`build-board.sh` 中**共存无丢失** | | **推送** | `git push origin master`(AF、camera-pipeline) | 成功:AF `3d8756ff..d4c3f39b`、camera-pipeline `38be214..bf9881a`;`versions.lock` 已更新为 clean 基线 | | **dataset_pdaf 接入** | `scripts/bootstrap.sh --only dataset_pdaf` | 本地播种(hardlink 对象库 + LFS 缓存)21s 完成,未下载 434M;`lfs fsck --pointers` 通过 | | **AF 共享数据根** | `make env` / `make status` | `AF_SWEEP_REGRESSION_SHARED_ROOT=/home/liqinxing/Camera/dataset_pdaf/datasets`;五仓齐、`dataset_pdaf` main clean | | **数据集内容校验** | 对照数据集仓 `扫峰验证指南.md` 与 AF `sweep-regression/README.md` 的 SHA-256 表 | 9 个 tar 全部一致(`light-strip` `882506e4…`、`moire` `8c9055a4…`、`dense-stripes` `607c7539…`、`face` `c396d73f…`、`point-light` `a661a2b0…`、`code` `e06517db…`、`low-texture` `67dec633…`、`high-texture` `a38553be…`、`low-light` `5f68fa68…`) | | **数据集端到端(AF 扫峰回归)** | `source env.sh` 后 `cd AF-Algorithm && bash tools/scripts/sweep-regression.sh` | 9/9 场景 `[PASS]`(70s);日志为 `[SHARED] … Camera/dataset_pdaf/datasets//.tar`(证明确实走共享目录、非 `$HOME`);把 `AF_SWEEP_REGRESSION_SHARED_ROOT` 指向不存在目录时按预期报 `[MISSING]` | | **worktree 冒烟(含新重仓)** | `make wt-new NAME=smoke` → `make wt-status NAME=smoke` → `make wt-rm NAME=smoke ARGS=--force` | 轻仓各自 `wt/smoke` worktree、重仓(vendor/open_source/dataset_pdaf)symlink 共享;remove 无需 `--force` 即干净删除。顺带修掉 `worktree.sh` 两个既有缺陷:`cmd_status` 在 `set -u` 下的未绑定变量(`local name="$1" wt="$WT_ROOT/$name"` 同一语句)、总仓残留分支导致 `worktree add -b` 直接失败 | | **worktree 内的数据集共享根** | `make wt-new NAME=dscheck ARGS='--base master'` 后在工作区内 `source env.sh` 并跑单场景扫峰回归(借用主树 `AF_SWEEP_REGRESSION_{SIMULATOR,CONFIG}`) | 解析为 `worktrees/dscheck/dataset_pdaf/datasets`(经 symlink 指向主工作区数据包);`[SHARED] …/worktrees/dscheck/dataset_pdaf/datasets/code/code-20260907.tar` → `[PASS]`;`make wt-rm` 干净删除 | | **AF 提交门禁数据根**(子仓 `0e8f9249`) | AF 里 `bash tools/scripts/host-validate.sh --commit`,以及真实 `git commit`(hook 自动执行);rebase 上游 `822acfca` 后重跑 | 全绿(format/C++11/覆盖率/face-tracking/sweep/3519D 交叉;rebase 前 1m51s、后 3m18s);`build/host-coverage/pre-commit-sweep.log` **9/9 `[SHARED] /home/…/Camera/dataset_pdaf/datasets/…` `[PASS]`**,日志中已无旧 `$HOME` 路径 | | **AF 数据根解析矩阵**(子仓 `0e8f9249`/`465c71ed`) | 主仓与树外 worktree(`/tmp/af-ds-wt`)× 三个入口(`sweep-regression.sh` / `sweep_regression.py` / `build_head_detector_calib.py --scene-root`),加解析器单测与 shell↔python 一致性比对 | 全部解析为 `Camera/dataset_pdaf/datasets`(树外 worktree 走 `git-common-dir` 反查主克隆);无总仓、总仓缺数据目录、`env_name=None` → `$HOME/dataset_pdaf/datasets`;`AF_SWEEP_REGRESSION_SHARED_ROOT`/`CAMERA_DEPS_BASE_DIR` 语义与 shell 侧 `camera_deps_root` 输出一致 | | **AF 数据根改动的回归**(子仓 `465c71ed`) | `git commit`(hook 自动跑 `host-validate.sh --commit`)+ `SweepRegression{Gate,Parallel}Test` | 门禁全绿(与前一提交同样覆盖 format/C++11/覆盖率/face-tracking/sweep/3519D 交叉),`pre-commit-sweep.log` 9/9 `[SHARED] Camera/dataset_pdaf/datasets` `[PASS]`;Python 单测 19/19 通过 | | **跨 harness skill 仓接入** | `make bootstrap`(二次)/ `make agent-skills` / `ARGS=--update` / `--relink` / `--dsh-link` / `--skip` / `--soft` / `make agent-skills-status` | 全部符合预期:已存在→只打印状态(含 skill 清单);`--update` 领先时提示未推送、落后时 fast-forward(在 `/tmp` 克隆里回退 1 个提交验证);路径被非 Git 目录占用时严格失败(exit 1),`--soft` 降级为警告(exit 0);**全新克隆 9.8s 完成**(模拟新机器,SSH BatchMode);`bootstrap` 二次执行 6.7s 幂等,末尾自动带上这一步 | | **总仓脚本回归(本次改动)** | `make status` / `make check-paths` / `make doctor` / `make check-deps` / `bash -n scripts/*.sh env.sh` | 全通过:`status` 表格末尾单列 `agent-skills user main -0 +1 0 ~/.agents/skills`;`doctor` 新增「跨 harness skill 仓」小节(`[ok] …1 个 skill`,缺失时为 `[note]` 不判 FAIL);`check-deps` 7/7 不变 | | **worktree 冒烟(含新脚本)** | `make wt-new NAME=smoke` → 工作区内 `make agent-skills` → `make wt-status NAME=smoke` → `make wt-rm NAME=smoke ARGS=--force` | 工作区(`85a51c7`)里 `scripts/agent-skills.sh` 存在,且解析为**全局** `~/.agents/skills`(不随工作区走);轻仓 `wt/smoke`、重仓 symlink 共享;remove 干净删除。附带发现既有行为:若 `wt/` 分支是残留(smoke 上次 `wt-rm` 未加 `--delete-branch`),`wt-new` 会复用它 → 工作区落在旧提交(19 个提交前),与本次改动无关,已在 §7 记录 | | **skill 仓布局改为「真源 + 契约软链」** | `make agent-skills`(自动迁移)→ 三边直接复核(`opencode debug skill` / pi 新会话 `/tmp` 下的 `` / dsh provider node 调用)→ `make status`/`check-paths`/`doctor`/`check-deps` → worktree 冒烟 | 迁移:`~/.agents/skills`(实体仓) → `~/Camera/skills`,原路径改为软链。三边 harness 在软链下均发现:**pi 真实会话系统提示** `` 里 `location=~/.agents/skills/hisi-kb-search/SKILL.md`;`opencode debug skill` 同一路径;dsh provider rank 500 同一路径(rank 400 的 `~/.dsh/skills/hi35xx-kb-search` 仍有效)。**worktree 语义实测**:工作区内 `CAMERA_MAIN_ROOT=/home/liqinxing/Camera`、`CAMERA_AGENT_SKILLS_REPO` 跟随主克隆,`make agent-skills` 不在工作区里克隆第二份。回归:`status`(新增契约软链行)/`check-paths` 9 项/`doctor` 通过/`check-deps` 7 项/`bash -n` 全通过 | | **`.agents/skills` 布局否决(附带实验)** | 临时把真源软链到 `~/Camera/.agents/skills` → 跑 pi `hasTrustRequiringProjectResources()` / dsh provider / opencode `debug skill`(测完即清理) | pi:Camera 变成“需要信任的项目”(每个会话弹信任);dsh:同名 **3 条**(200 项目 + 400 + 500);opencode:不重复、无信任门槛,但上报 location 随 cwd 漂移(进 `AF-Algorithm` 子仓又回落 home)。结论:保持 `~/Camera/skills`(理由见 §3.2) | | **skills 升格为第 6 个子仓 + 迁移 gitcode** | `git remote set-url` + `git push -u origin main` → `make status`/`doctor`/`snapshot` → worktree 冒烟 → 全新 clone 测试 → `make check-deps` | gitcode 空仓(0 refs)推送成功:`14c8f2f`,`ls-remote` 两端一致;`repos.tsv` 加一行(`main` / `-` / `heavy`,真 TAB 校验 6 字段)后 `status` 正常列出(不再有重复行)、`doctor` 的「子仓库」小节含 skills、`make snapshot` 只新增这一行(其余子仓 commit 未变,仅时间戳刷新);worktree 里 `skills → ../../skills` symlink(kind=heavy),`env.sh` 解析真源仍为主副本;从新远端全新 `git clone` 通过;`make check-deps` 7/7 不受影响;旧 gitee 远端按选择移除 |