---
name: harness-knowledge-ingest
description: "确认归档 ZIP 已上传并由 Hunter Platform 在服务端解包、校验和入库。客户端不再构建或维护本地知识索引。"
---
<!-- generated by harness_deploy.py; core=453b5642e5b9134d; agent=codebuddy; do not edit -->
# harness-knowledge-ingest

知识 ingest 由 Hunter Platform 负责：归档完成时，客户端生成一个确定性 ZIP
（含 `candidates/knowledge.json` 候选清单——由本包 `harness_knowledge_candidates.py`
从 design/plan/test-scenarios 与归档 summary 派生）；服务端收到后保存原包、
安全解包、发布核心文件，按候选抽取知识条目并重建项目语义索引。

客户端不得生成 `.harness/knowledge`、SQLite 索引、视图、报告或本地裁决。
历史版本数据只由升级兼容逻辑识别，不再执行。

## 归档包边界

允许上传的核心内容只有：

- `reports/final/summary-data.json`
- `spec/**/*.md`
- `plans/**/*.md`
- `archive-meta.md`
- `change-context.json`
- 包内 `archive-manifest.json`（由 CLI 生成）

日志、测试报告、审查报告、HTML、缓存、备份、凭据和临时文件不得进入归档包。

## Normal Workflow

1. 先读取最近归档操作记录中的 `archiveRemote`，以及本地状态目录
   `.harness/state/local/archive-packages/<change-key>.remote.json`。若 `archiveStatus=durable` 且
   `knowledgeStatus=ready`，直接报告已完成，禁止重新打包或上传。
2. 若存在 `.harness/state/local/archive-packages/<change-key>.zip`，只重试该包；
   禁止搜索实现源码、调用 Python 内部函数或手工重新拼包。
3. 只有旧归档没有 ZIP/回执时，调用公开命令重新生成并上传；`harness-archive`
   的 finalize 正常情况下会生成确定性 ZIP，并调用：

```powershell
powershell.exe -Command "npx hunter-harness archive upload --file '<archive.zip>' --change-key '<change-key>' --yes --non-interactive --json"
```

4. 检查响应中的包哈希、服务端保存状态和 `knowledge_status`。
4.5. **查询面回读验证（强制）**：`knowledgeStatus=ready` 只是入库回执，不代表可查。
    执行 `npx hunter-harness knowledge status --json` 确认 `pipeline.results_count`
    符合预期（有候选的归档应 > 0）；再用归档中的已知关键词跑一次
    `npx hunter-harness knowledge query "<已知关键词>" --json`，`count=0` 视为
    ingest 未真正完成——报告 `pipeline.jobs` 状态（queued/extracting/failed）
    并停止宣称 ready，不得仅凭上传回执收尾。
5. 只有服务端确认原 ZIP 已持久保存且知识状态为 ready，才删除本地待上传 ZIP。
6. 上传或 ingest 失败时保留 ZIP 与失败收据；修复连接后重试同一个 ZIP，不重新拼散文件。

## Ownership Rules

- 客户端：选择核心文件、生成稳定 manifest/ZIP、校验包哈希、上传与保留失败重试材料。
- 服务端：敏感信息扫描、ZIP 安全校验、原包持久化、解包、制品发布、知识 ingest、状态查询和下载恢复。
- 远端不可用：知识不可用；不得启动本地替代索引。
- 面向人的知识、提案、规则和说明默认使用中文。

## Forbidden Actions

- build_or_refresh_local_knowledge
- write_dot_harness_knowledge
- upload_logs_reviews_tests_or_temp_files
- call_legacy_python_ingest
- treat_upload_acceptance_without_durable_status_as_success
- delete_pending_zip_after_failed_upload

## Verification

确认归档命令的 JSON 结果同时包含：服务端 package 哈希、保存状态和知识状态。
若失败，确认 `.harness/state/local/archive-packages/` 中仍保留可重试 ZIP；若成功，确认
ZIP 已按收据策略清理，且平台下载接口可以恢复原包。

## P0 执行可信度规则

- 命令结果不得靠猜测；普通 Bash 被拒 → 立即改用等价 PowerShell 重试一次
- 仅 PowerShell 成功且有明确证据（构建/git/测试输出、文件存在、exit 0）时可标 ✅OK；否则 ❌FAIL 或 🟡WARN
- 禁止把 hook 拒绝、静态验证、无输出、用户跳过说成成功 → 详见 [[../protocols/powershell-protocol.md|powershell-protocol]]、[[../protocols/evidence-based-reporting-protocol.md|evidence-based-reporting-protocol]]

## 生成内容语言约定

- sync/ingest 等生成的文档、规则、知识条目、架构说明一律**优先使用中文**撰写（标识符、命令、代码、API 字段名保持原文）
- 面向平台展示的标题/摘要/正文默认中文；仅当用户明确要求或目标系统强制时才用英文
> 片段：p0-trust · 只有服务端持久化与 ingest 收据可证明知识已入库

## 执行日志

`events.ndjson` 为唯一事实源（schema_version 3，兼容读取 v1/v2；`note` 承载人类可读摘要）；`logs/execution-log.md` 由 `harness_events.py` 渲染，**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖，属于数据丢失；需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]

**`phase.start` 由 `harness_gate.py begin` 写，不要再手工追加一次。** 两条同 `run-id` 的
`phase.start` 会让 `plan finalize` 以 `PHASE_START_DUPLICATE` 卡死，而且手工那次会先触发
auto-seal、把正在开始的 attempt 封成 `RECOVERED`。要补触发指令说明就带 `--note` 跑 `gate begin`。
（重复追加现已按 `(phase, run-id)` 判为幂等 no-op，但依赖它不如不写。）

```powershell
# 阶段开始：gate begin 负责，note 在这里给
python <skills-root>/scripts/harness_gate.py begin --change-dir ".harness/changes/<change-name>" --phase <phase> --note "<触发指令>"
# 阶段中的其他事件才用 append
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type <command|issue|verification> --run-id <phase-run-id> --note "<摘要>"
```

> **脚本接线**：`harness_events.py append`；`harness_archive.py finalize`；`harness_preflight.py check`；`harness_ledger.py can-reuse`；`harness_service.py ensure/stop`（须 `--files`/`serviceStart.inputFiles`）。JSON 输出按 D13 护栏解读。

> **Task 4 §6.1 写入契约**：普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁，**不 load 历史、不渲染**（O(1)，跨进程锁 `events.ndjson.lock`，UUID 用完整 `uuid4().hex` 无需去重扫描）。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`；显式 `harness_events.py render` 随时从完整 events 重建；`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后，phase 边界保持最新。

每个阶段的 `phase.start` 与对应 `phase.end` 必须复用同一 `--run-id` / `--attempt`；阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`。重试同一阶段时生成新的 run-id 并增加 `--attempt <n>`，不得覆盖或伪装成一次执行。**`attempt` 按 phase 全局递增，不是按 run-id**：一个 run-id 只绑定一个 attempt，重试必须「新 run-id ＋ 下一个 attempt」两者同时换，只换其一会撞 `EVENT_ATTEMPT_CONFLICT` 或 `PHASE_ALREADY_CLOSED`。已发布 plan 的修订通过重跑 `plan evidence-pack` + `plan finalize` 分配新 attempt（`harness_plan_finalize.py republish` 已于 0.3.0 移除）。

阶段跑得久（plan/run 常见）时用 `harness_context.py renew --project . --change <cn> --executor <tool>` 续租；租约到期本身不再阻断 `close`（同一 owner 的过期租约不构成冲突，收据里记 `leaseLapsed`），但续租能让 `view` 的状态如实反映在跑。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`，并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`；也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
> 片段：logging · phase=`knowledge-ingest` · 记录 package hash、服务端状态与失败重试路径
