Просмотр исходного кода

docs: add V4 M4 implementation briefs

Sam Lee 1 неделя назад
Родитель
Сommit
e7290e79f4

+ 200 - 0
tech_documents/工程落地/v4_implementation_briefs/M4/00_M4_Brief_Index.md

@@ -0,0 +1,200 @@
+# V4 M4 游走消费 allow_walk Brief Index
+
+状态:本目录覆盖 V4 M4——把 M3 已产出的 `decision_replay_data.allow_walk` 从“记录字段”升级为 `walk_engine` 的实际游走准入口径。M4 只规划游走消费、walk runtime 可回放证据、walk policy/config validator 和 replay 验收;不修改 M3 Gemini/评分、不修改 M5 Web/API、不新增 DB 表或正式列。
+
+## 目标定位
+
+M4 的目标是让所有扩展游走都能被 M3 决策产物解释和回放:
+
+1. `allow_walk=true` 成为 V4 内容出边的唯一准入信号。
+2. `query_next_page`、`hashtag_to_query`、`author_to_works` 都必须消费 `allow_walk`。
+3. `allow_walk=false` 的 V4 内容不得触发翻页、tag 回灌或作者作品拉取。
+4. `walk_actions.raw_payload` 必须保留 `allow_walk/allow_walk_reason/walk_gate_snapshot` 消费证据。
+5. runtime validator 能跨文件证明 walk action 与 V4 decision gate 一致。
+6. DB/runtime 继续复用 JSON 容器,不新增 DB 表或正式列。
+
+M4 完成后,M5 可以直接解释“为什么这条内容没有继续游走”,而不需要重新计算 M3 分数或反推 `decision_action`。
+
+## 子 brief 执行口径(A→E 线性)
+
+| 子 brief | 文件 | 核心交付 | 依赖 |
+|---|---|---|---|
+| **M4A** | `M4A_Allow_Walk_Consumer_And_Edge_Gate_Contract.md` | `walk_engine` 读取 `allow_walk` 的统一 helper 与 edge permission 合同 | M3 scorecard producer、现有 `walk_engine` |
+| **M4B** | `M4B_Query_Next_Page_Tag_And_Author_Walk_Gate_Contract.md` | 翻页、tag 回灌、作者作品三条边的 V4 gate 消费细节 | M4A、walk policy、platform profiles |
+| **M4C** | `M4C_Walk_Policy_JSON_Excel_Validator_Contract.md` | `walk_policy.json` / 游走 Excel / config validator 的 M4 gate 合同 | M4A-M4B、现有 config gate |
+| **M4D** | `M4D_Runtime_Validation_DB_And_Source_Path_Replay_Contract.md` | runtime validator、DB JSON 容器、source path / walk action 可回放合同 | M4A-M4C、DB runtime |
+| **M4E** | `M4E_Acceptance_Commands_And_Subagent_Audit.md` | Unit Test、Integrated Test、DB 只读验证、config gate、Subagent 审计汇总 | M4A-M4D |
+
+## 不处理范围(红线)
+
+- 不改 M3 Gemini prompt、parser、technical failure 策略。
+- 不改 M3 `query_relevance_score/platform_performance_score/score` 阈值。
+- 不改 M3 `allow_walk=true` 阈值:`query>=70 && platform>=65 && score>=70`。
+- 不改 M2 平台 client、搜索 limit、快手接入、视频号 retry、抖音/视频号/快手抓取能力。
+- 不新增作者画像评分、tag relevance LLM、二跳回灌或新平台能力。
+- 不改 M5 final output、strategy review、dashboard/Web/API 展示解释。
+- 不新增 DB 表或正式列;M4 消费证据进入 JSON 容器。
+- 不把 `decision_action=ADD_TO_CONTENT_POOL` 继续等价为“可游走”。
+- 不破坏非 V4 历史 decision 的旧 `decision_action + edge_permissions` 行为。
+- 文档阶段只新增本目录 Markdown;不修改业务代码、DB schema、JSON 配置或 Excel。
+
+## 现有证据(共享,子 brief 各自细引)
+
+- 代码线 subagent 只读审计:`content_agent/business_modules/rule_judgment/evaluator.py::_decide_v4` 已计算 `allow_walk = _v4_allow_walk(...)`,并写入 `decision_replay_data`。
+- 代码线 subagent 只读审计:`evaluator._v4_allow_walk(...)` 阈值是 `query >= 70`、`platform >= 65`、`score >= 70`。
+- 代码线 subagent 只读审计:`content_agent/business_modules/run_record/validation.py::_check_v4_walk_gate_contract` 只校验 `allow_walk` 存在、bool、true 阈值,不校验 `walk_actions` 是否遵守。
+- 代码线 subagent 只读审计:`content_agent/business_modules/walk_engine.py::_edge_permission_for` 当前只看 `decision_action` 和 `search_query_effect_status == rule_blocked`,未读取 `decision_replay_data.allow_walk`。
+- 代码线 subagent 只读审计:`walk_engine._can_fetch_next_page(...)` 当前只要求 query effect 为 `success`;`query_next_page` 可能在 `allow_walk=false` 时继续翻页。
+- 代码线 subagent 只读审计:`tests/test_rule_pack_reading.py` 已有 `query=80/platform=60/score=70` 的 V4 case,`decision_action=ADD_TO_CONTENT_POOL` 但 `allow_walk=false`;这是 M4 必须消费的关键反例。
+- JSON/config 线 subagent 只读审计:`product_documents/规则包/douyin_rule_packs.v1.json` 与规则包 Excel 已切到 V4 scorecard,M4 不需要再改主 rule pack 评分口径。
+- JSON/config 线 subagent 只读审计:`tech_documents/数据接口与来源/walk_policy.json` 仍只有 `edge_permissions`,按 `ADD_TO_CONTENT_POOL/KEEP_CONTENT_FOR_REVIEW/REJECT_CONTENT` 放行,没有 `requires_allow_walk` 或 `v4_allow_walk_denied` 配置。
+- JSON/config 线 subagent 只读审计:`tech_documents/游走策略/游走策略配置表.xlsx` 仍只有 `walk_edge_catalog`、`walk_rule_pack_binding`、`walk_fact_contract`,未检出 `allow_walk/walk_gate_snapshot`。
+- JSON/config 线 subagent 只读审计:三平台 profile 当前状态为抖音 page/author/tag supported,快手 page/author blocked 但 tag supported weak,视频号 page/tag supported 但 author blocked。
+- DB/runtime 线 subagent 只读审计:`content_agent_rule_decisions` 已有 `scorecard/decision_replay_data/raw_payload` JSON 列;`content_agent_walk_actions`、`content_agent_source_path_records`、`content_agent_search_clues` 均有 `raw_payload` JSON。
+- DB/runtime 线 subagent 只读审计:`content_agent/integrations/database_runtime.py::JSON_COLUMNS_BY_TABLE` 已把 `decision_replay_data`、`walk_actions.raw_payload`、`source_path_records.raw_payload`、`search_clues.raw_payload` 列为 JSON 容器。
+- DB/runtime 线 subagent 只读验证:`uv run python scripts/validate_schema_registry.py` 通过;`uv run pytest -q -p no:cacheprovider tests/test_database_runtime.py tests/test_v4_m3_scoring_replay.py tests/test_walk_actions_runtime.py` 通过 37 个测试。
+- DB/runtime 线 subagent 只读验证:`validate_content_agent_db.py --allow-missing-tables` 本轮连接阶段返回 MySQL `2013 Lost connection to MySQL server during query`,M4 实现验收必须重跑。
+
+## 数据合同(汇总,子 brief 各自细化)
+
+M4 消费的 V4 decision replay:
+
+```json
+{
+  "decision_id": "d_001",
+  "decision_action": "ADD_TO_CONTENT_POOL",
+  "scorecard": {
+    "schema_version": "v4_scorecard.v1",
+    "query_relevance_score": 80,
+    "platform_performance_score": 60,
+    "total_score": 70
+  },
+  "decision_replay_data": {
+    "allow_walk": false,
+    "allow_walk_reason": "v4_query_and_platform_pass",
+    "walk_gate_snapshot": {
+      "query_relevance_score": 80,
+      "platform_performance_score": 60,
+      "score": 70
+    }
+  }
+}
+```
+
+M4 `walk_actions.raw_payload` 消费证据:
+
+```json
+{
+  "decision_id": "d_001",
+  "decision_action": "ADD_TO_CONTENT_POOL",
+  "allow_walk": false,
+  "allow_walk_reason": "v4_query_and_platform_pass",
+  "walk_gate_snapshot": {
+    "query_relevance_score": 80,
+    "platform_performance_score": 60,
+    "score": 70
+  },
+  "walk_gate_status": "denied",
+  "walk_gate_reason_code": "v4_allow_walk_denied"
+}
+```
+
+M4 walk policy 建议结构:
+
+```json
+{
+  "v4_walk_gate": {
+    "requires_allow_walk": true,
+    "deny_reason_code": "v4_allow_walk_denied",
+    "applies_to_edges": [
+      "query_next_page",
+      "hashtag_to_query",
+      "author_to_works"
+    ],
+    "source_field": "rule_decisions.jsonl[].decision_replay_data.allow_walk"
+  }
+}
+```
+
+## M4 已拍板口径
+
+- V4 出边准入以 `decision_replay_data.allow_walk` 为准。
+- `allow_walk=false` 时不触发 `query_next_page/hashtag_to_query/author_to_works`。
+- 非 V4 decision 沿用现有 `decision_action + walk_policy.edge_permissions`。
+- 平台 profile blocked 优先于 allow_walk gate:blocked edge 显式 skip 且不调平台。
+- 预算耗尽仍使用 `budget_exhausted`,不要被 `v4_allow_walk_denied` 覆盖。
+- `walk_actions.raw_payload` 要写消费证据,便于 M5 解释。
+- M4 不重算 M3 分数,不读取 Gemini/platform raw 字段重新判断。
+- DB 不 migration;所有 M4 新证据进入 JSON 容器。
+- 开工前待拍板项:无,按 V4 正式计划已拍板口径执行。
+
+## 验证命令(汇总)
+
+```bash
+uv run pytest tests/test_walk_engine_author.py tests/test_walk_engine_tag.py tests/test_walk_engine_pagination.py -q
+uv run pytest tests/test_v4_walk_contract.py tests/test_v4_validator_contract.py tests/test_v4_m4_walk_replay.py -q
+uv run pytest tests/test_database_runtime.py tests/test_v4_m3_scoring_replay.py tests/test_walk_actions_runtime.py -q
+uv run pytest tests/test_case_replay.py tests/test_walk_profile_degradation.py -q
+uv run python scripts/validate_content_agent_db.py --allow-missing-tables
+uv run python scripts/validate_schema_registry.py
+uv run python scripts/validate_rule_pack_config.py
+uv run python scripts/validate_v4_config_contract.py
+uv run --with openpyxl python scripts/build_config_from_excel.py --check
+uv run --with openpyxl python scripts/validate_config_excel_sync.py
+python3 scripts/run_config_gate.py
+uv run pytest -q
+```
+
+## Unit Test(汇总)
+
+- `tests/test_walk_engine_author.py`
+  - `ADD_TO_CONTENT_POOL + allow_walk=false` 不调用 `fetch_author_works`。
+  - `allow_walk=false` 写 `v4_allow_walk_denied` skip。
+  - `allow_walk=true` 保持作者作品成功、去重、search query lineage。
+  - 非 V4 decision 保持旧行为。
+- `tests/test_walk_engine_tag.py`
+  - `allow_walk=false` 不生成 tag query。
+  - `allow_walk=true` 保留 tag query。
+  - blocked tag、预算耗尽、profile blocked 行为不变。
+- `tests/test_walk_engine_pagination.py`
+  - `success + has_more + cursor + allow_walk=false` 不生成 page query。
+  - `allow_walk=true` 仍生成 page query。
+- `tests/test_v4_walk_contract.py`
+  - V4 gate config 存在。
+  - deny reason 为 `v4_allow_walk_denied`。
+  - 三平台 edge status 仍为 `supported/blocked`。
+- `tests/test_v4_validator_contract.py`
+  - validator 拒绝 `allow_walk=false` 却产生成功扩展 action。
+  - validator 拒绝 V4 walk action 缺 `walk_gate_snapshot` 消费证据。
+
+## Integrated Test
+
+- 新增 `tests/test_v4_m4_walk_replay.py`。
+- 构造两条 V4 decision:
+  - `ADD_TO_CONTENT_POOL + allow_walk=true`。
+  - `ADD_TO_CONTENT_POOL + allow_walk=false`。
+- 运行 `run_bounded_walk(...)`。
+- 验证只有 `allow_walk=true` 内容触发 page/tag/author 扩展。
+- 验证 `allow_walk=false` 内容产生 `v4_allow_walk_denied` skip 或 path stop,不调用平台作者接口。
+- 用 `LocalRuntimeFileStore + validate_run()` 验证 runtime 通过。
+- 用 `DatabaseRuntimeStore` fake connection 写入 `rule_decisions/walk_actions/source_path_records/search_clues/search_queries`,验证 raw_payload 保留 M4 gate 证据。
+- 保持快手/视频号 profile blocked replay 通过:profile blocked 优先,不因 allow_walk 误调用 blocked 作者作品接口。
+
+## 失败归因(汇总)
+
+- `allow_walk=false` 仍触发作者作品:先查 `_edge_permission_for(...)` 是否识别 V4 scorecard。
+- `allow_walk=false` 仍触发翻页:先查 `_query_effect_by_search_query_id(...)` 与 `_can_fetch_next_page(...)` 是否读取 query 下 allow_walk。
+- tag skip reason 错误:先查 `video_to_hashtag` deny 是否区分 `v4_allow_walk_denied`、`review_tag_expansion_disabled`、`blocked_by_rule_decision`。
+- 平台 blocked 被误调用:先查 `edge_supported(profile, edge_id)` 是否仍在 allow_walk gate 之前。
+- 预算 skip 被覆盖:先查预算判断顺序,预算耗尽仍应写 `budget_exhausted`。
+- validator 未发现错误扩展:先查是否将 walk action 与 decision id / content id 建立关联。
+- DB 写入后证据丢失:先查 `walk_actions.raw_payload` 与 `source_path_records.raw_payload` 是否包含 gate context。
+- config gate 未覆盖 M4:先查 `validate_v4_config_contract.py` 是否校验 `v4_walk_gate`。
+- DB 只读验证失败:本轮已见 MySQL 2013 断线,先重跑 `validate_content_agent_db.py --allow-missing-tables` 并记录结果。
+
+## sub-agent 交叉验证要点
+
+- 旧 subagent 关闭请求:`019ec6e4-8ae9-7fb3-ae13-b9ed17baa2ae`、`019ec6e4-8b67-77d3-a996-a55c790342a5`、`019ec6e4-8bd1-7d90-91cd-89ed292fb7fe` 均返回 `not found`。
+- 新代码线 subagent:`019ec71d-079d-7f81-821e-ff9b9cac4498`(Bohr)确认 M3 已产 `allow_walk`,M4 `walk_engine` 未消费,最大缺口是 `query_next_page` 仍只看 query effect。
+- 新 JSON/config 线 subagent:`019ec71d-2a9c-7f61-af9c-3963a56afe8a`(Newton)确认 rule pack 已 V4,walk policy/游走 Excel 缺 M4 allow_walk gate,config validator 需补强。
+- 新 DB/runtime 线 subagent:`019ec71d-4904-7212-8d45-2d9e0504aa13`(Nietzsche)确认 DB 不需要 migration,JSON 容器足够,但 DB validator 本轮 MySQL 2013 断线,验收必须重跑。

+ 174 - 0
tech_documents/工程落地/v4_implementation_briefs/M4/M4A_Allow_Walk_Consumer_And_Edge_Gate_Contract.md

@@ -0,0 +1,174 @@
+# M4A Allow Walk Consumer And Edge Gate Contract Implementation Brief
+
+状态:本 brief 覆盖 M4 的统一 `allow_walk` 消费合同。M4A 只规划 `walk_engine` 如何从 V4 rule decision 读取 `decision_replay_data.allow_walk`,并把它接入 edge permission;不改 M3 评分、不改平台抓取、不改 DB schema。
+
+## 目标
+
+让 V4 内容出边不再由 `decision_action=ADD_TO_CONTENT_POOL` 直接放行,而是由 M3 的 `allow_walk` 决定:
+
+1. V4 decision 的 `scorecard.schema_version == "v4_scorecard.v1"` 时,必须读取 `decision_replay_data.allow_walk`。
+2. `allow_walk=false` 时,所有扩展边返回 deny。
+3. deny reason 统一为 `v4_allow_walk_denied`。
+4. `walk_actions.raw_payload` 写入 `allow_walk` 消费证据。
+5. 非 V4 decision 保持现有旧行为,避免影响历史 runtime/replay。
+
+## 现有证据
+
+- `content_agent/business_modules/rule_judgment/evaluator.py::_decide_v4(...)` 已计算 `allow_walk`。
+- `evaluator._v4_allow_walk(...)` 已钉死 `query>=70 && platform>=65 && score>=70`。
+- `evaluator._v4_walk_gate(...)` 已写 `allow_walk`、`allow_walk_reason`、`walk_gate_snapshot`。
+- `content_agent/business_modules/run_record/validation.py::_check_v4_walk_gate_contract(...)` 已校验 producer 字段,但不校验 walk consumer。
+- `content_agent/business_modules/walk_engine.py::_edge_permission_for(...)` 当前只看 `decision_action` 和 `search_query_effect_status == "rule_blocked"`。
+- `walk_engine._decision_context(...)` 当前只为 raw payload 提供 decision 基本上下文,未注入 `allow_walk/walk_gate_snapshot`。
+- `tests/test_rule_pack_reading.py` 证明 V4 存在 `ADD_TO_CONTENT_POOL` 但 `allow_walk=false` 的合法记录。
+
+## 修改范围
+
+- 后续实现中可修改:
+  - `content_agent/business_modules/walk_engine.py`
+  - `tests/test_walk_engine_author.py`
+  - `tests/test_walk_engine_tag.py`
+  - `tests/test_walk_engine_pagination.py`
+  - `tests/test_v4_walk_contract.py`
+- 后续实现中可新增极小 helper:
+  - `_is_v4_decision(decision)`
+  - `_v4_allow_walk_allowed(decision)`
+  - `_v4_walk_gate_context(decision)`
+  - `_v4_walk_gate_denied(decision)`
+
+## 不修改范围
+
+- 不修改 `rule_judgment/evaluator.py` 的 M3 scorecard 与阈值。
+- 不修改 `gemini_video.py`。
+- 不修改平台 client。
+- 不新增 DB 列。
+- 不改 Web/API。
+- 不把 `allow_walk` 逻辑散落到多个调用点重复实现。
+
+## 涉及文件 / 函数 / 类
+
+- `content_agent/business_modules/walk_engine.py`
+  - `run_bounded_walk(...)`
+  - `_expand_queries(...)`
+  - `_expand_authors(...)`
+  - `_edge_permission_for(...)`
+  - `_decision_context(...)`
+  - `_walk_action(...)`
+  - `_author_walk_action(...)`
+  - `_execution_record(...)`
+- `content_agent/integrations/walk_graph_json.py`
+  - `edge_permission(...)`
+  - `edge_supported(...)`
+- `content_agent/business_modules/run_record/validation.py`
+  - `_check_v4_walk_gate_contract(...)`
+- `tests/test_walk_engine_author.py`
+- `tests/test_walk_engine_tag.py`
+- `tests/test_walk_engine_pagination.py`
+- `tests/test_v4_walk_contract.py`
+
+## 数据合同
+
+V4 decision gate source:
+
+```json
+{
+  "scorecard": {
+    "schema_version": "v4_scorecard.v1"
+  },
+  "decision_replay_data": {
+    "allow_walk": false,
+    "allow_walk_reason": "v4_query_and_platform_pass",
+    "walk_gate_snapshot": {
+      "query_relevance_score": 80,
+      "platform_performance_score": 60,
+      "score": 70
+    }
+  }
+}
+```
+
+`_v4_walk_gate_context(decision)` 建议返回:
+
+```json
+{
+  "decision_id": "d_001",
+  "decision_action": "ADD_TO_CONTENT_POOL",
+  "allow_walk": false,
+  "allow_walk_reason": "v4_query_and_platform_pass",
+  "walk_gate_snapshot": {
+    "query_relevance_score": 80,
+    "platform_performance_score": 60,
+    "score": 70
+  },
+  "walk_gate_status": "denied",
+  "walk_gate_reason_code": "v4_allow_walk_denied"
+}
+```
+
+## 实施步骤
+
+1. 在 `walk_engine.py` 内新增 `_is_v4_decision(decision)`:
+   - `decision` 为空返回 false。
+   - `decision.scorecard.schema_version == "v4_scorecard.v1"` 返回 true。
+2. 新增 `_v4_allow_walk_allowed(decision)`:
+   - 非 V4 返回 `None`,表示调用方继续走旧逻辑。
+   - V4 且 `decision_replay_data.allow_walk is True` 返回 true。
+   - V4 且 `allow_walk` 缺失或不是 true 返回 false。
+3. 修改 `_edge_permission_for(decision, edge_id, policy)`:
+   - 无 decision 或 `search_query_effect_status == "rule_blocked"` 仍 deny。
+   - V4 decision 且 `_v4_allow_walk_allowed(...) is False` 返回 deny。
+   - V4 decision 且 allow true 再走现有 `edge_permission(...)`。
+   - 非 V4 保持现有行为。
+4. 新增 `_v4_walk_gate_context(decision)`:
+   - 从 `decision_replay_data` 拷贝 `allow_walk/allow_walk_reason/walk_gate_snapshot`。
+   - 添加 `walk_gate_status`:`allowed` 或 `denied`。
+   - 添加 `walk_gate_reason_code`:deny 时 `v4_allow_walk_denied`。
+5. 修改 `_decision_context(decision)`:
+   - 保留现有字段。
+   - 若是 V4 decision,合并 `_v4_walk_gate_context(...)`。
+6. 修改所有因 `_edge_permission_for(...) == "deny"` 写 skip 的地方:
+   - 若 V4 allow false,`reason_code="v4_allow_walk_denied"`。
+   - 其它旧 deny 继续用现有 reason。
+7. 确保 `_walk_action(... raw_extra=...)` 能把 gate context 写入 `raw_payload`。
+8. 确保 `_execution_record(...)` 不改变 rule pack fallback:仍写 content pack rule id/version。
+
+## 验证命令
+
+```bash
+uv run pytest tests/test_walk_engine_author.py tests/test_walk_engine_tag.py tests/test_walk_engine_pagination.py -q
+uv run pytest tests/test_v4_walk_contract.py -q
+```
+
+## Unit Test
+
+- `_edge_permission_for`:
+  - V4 `allow_walk=false` + `ADD_TO_CONTENT_POOL` + edge `author_to_works` → deny。
+  - V4 `allow_walk=true` + `ADD_TO_CONTENT_POOL` + edge `author_to_works` → allow。
+  - V4 `allow_walk=true` + `REJECT_CONTENT` → deny,仍受 `edge_permissions` 约束。
+  - non-V4 `ADD_TO_CONTENT_POOL` → 旧 allow 行为不变。
+  - `search_query_effect_status=rule_blocked` → deny 优先。
+- `_decision_context`:
+  - V4 decision 包含 `allow_walk`、`allow_walk_reason`、`walk_gate_snapshot`。
+  - non-V4 decision 不强制这些字段。
+- skip reason:
+  - V4 allow false 输出 `v4_allow_walk_denied`。
+  - review tag deny 仍输出 `review_tag_expansion_disabled`。
+  - reject deny 非 V4 仍输出 `blocked_by_rule_decision`。
+
+## Integrated Test
+
+- 用 `tests/p6_walk_helpers.py::build_initial_walk_context(...)` 构造一个 V4 allow true 内容,确认现有 author/tag/page 行为保留。
+- 手工把该 decision 的 `allow_walk` 改 false,运行 `run_bounded_walk(...)`,确认所有扩展边被 deny。
+
+## 失败归因
+
+- allow false 仍扩展:先查 `_is_v4_decision(...)` 是否识别 `scorecard.schema_version`。
+- 非 V4 旧测试失败:先查 `_edge_permission_for(...)` 是否对 non-V4 返回了 deny。
+- skip reason 不稳定:先查 reason code 是否在每个 skip 分支重复硬编码,应统一从 helper 取。
+- raw payload 缺 gate:先查 `_decision_context(...)` 是否被传入 `_walk_action(... raw_extra=...)`。
+
+## sub-agent 交叉验证要点
+
+- 代码线:确认 M4A 只改 `walk_engine` gate,不回改 M3 producer。
+- DB/runtime 线:确认 gate context 落在 `walk_actions.raw_payload`,不需要正式列。
+- JSON/config 线:确认 deny reason 与后续 `walk_policy.v4_walk_gate.deny_reason_code` 一致。

+ 219 - 0
tech_documents/工程落地/v4_implementation_briefs/M4/M4B_Query_Next_Page_Tag_And_Author_Walk_Gate_Contract.md

@@ -0,0 +1,219 @@
+# M4B Query Next Page Tag And Author Walk Gate Contract Implementation Brief
+
+状态:本 brief 覆盖 M4 三条扩展边的 allow_walk 消费细节:`query_next_page`、`hashtag_to_query`、`author_to_works`。M4B 不新增边、不改预算数值、不改平台 client,只把 M4A 的统一 gate 接入现有边。
+
+## 目标
+
+让三条会产生新搜索或新内容的边都遵守 V4 `allow_walk`:
+
+1. `query_next_page` 不再只看 query effect `success`。
+2. `hashtag_to_query` 不再只看 `decision_action` 对 tag 的 permission。
+3. `author_to_works` 不再因 `ADD_TO_CONTENT_POOL` 自动调用作者作品接口。
+4. 平台 blocked、预算耗尽、去重、血缘补全保持现有行为。
+5. skip/action raw payload 能解释每条边为什么执行或不执行。
+
+## 现有证据
+
+- `walk_engine._expand_queries(...)` 当前负责 `query_next_page` 与 `hashtag_to_query`。
+- `query_next_page` 当前使用 `_query_effect_by_search_query_id(...)` 与 `_can_fetch_next_page(...)`;`_can_fetch_next_page(...)` 只判断 query effect 是否为 `success`。
+- `hashtag_to_query` 当前通过 `_edge_permission_for(decision, "video_to_hashtag", policy)` 判断。
+- `walk_engine._expand_authors(...)` 当前通过 `_edge_permission_for(decision, "author_to_works", policy)` 判断,并在 allow 时调用 `fetch_author_works(...)`。
+- `walk_engine._expand_authors(...)` 当前对 profile blocked edge 会显式 skip,不调用平台。
+- `walk_engine._expand_authors(...)` 已有作者作品去重与合成 `author_works` search query lineage。
+- `walk_policy.json` 当前 `edge_permissions.ADD_TO_CONTENT_POOL` 放行 `author_to_works/video_to_hashtag/decision_to_asset`。
+
+## 修改范围
+
+- 后续实现中可修改:
+  - `content_agent/business_modules/walk_engine.py::_expand_queries(...)`
+  - `content_agent/business_modules/walk_engine.py::_expand_authors(...)`
+  - `content_agent/business_modules/walk_engine.py::_query_effect_by_search_query_id(...)`
+  - `content_agent/business_modules/walk_engine.py::_can_fetch_next_page(...)`
+  - `content_agent/business_modules/walk_engine.py::_query_actions(...)`
+  - `content_agent/business_modules/walk_engine.py::_author_walk_action(...)`
+- 后续实现中可新增小 helper:
+  - `_walk_allowed_decisions_by_query(...)`
+  - `_walk_gate_reason_for_decision(...)`
+  - `_query_walk_gate_for_page(...)`
+
+## 不修改范围
+
+- 不新增 `query_next_page` 多页逻辑。
+- 不新增二跳 tag 回灌。
+- 不修改 `walk_policy.edge_budgets` 数值。
+- 不修改快手/视频号 blocked profile。
+- 不修改 platform client 的 `search/fetch_author_works` 接口。
+- 不修改 M3 rule decision 产物。
+
+## 涉及文件 / 函数 / 类
+
+- `content_agent/business_modules/walk_engine.py`
+  - `_expand_queries(...)`
+  - `_expand_authors(...)`
+  - `_query_effect_by_search_query_id(...)`
+  - `_can_fetch_next_page(...)`
+  - `_query_actions(...)`
+  - `_search_query_row(...)`
+  - `_author_walk_action(...)`
+  - `_walk_action(...)`
+  - `_unique_authors(...)`
+- `content_agent/integrations/walk_graph_json.py`
+  - `edge_supported(...)`
+  - `edge_permission(...)`
+- `tests/test_walk_engine_pagination.py`
+- `tests/test_walk_engine_tag.py`
+- `tests/test_walk_engine_author.py`
+- `tests/test_walk_profile_degradation.py`
+- `tests/test_case_replay.py`
+
+## 数据合同
+
+`query_next_page` skip action 建议:
+
+```json
+{
+  "edge_id": "query_next_page",
+  "walk_status": "skipped",
+  "reason_code": "v4_allow_walk_denied",
+  "raw_payload": {
+    "parent_search_query_id": "q_001",
+    "source_content_id": "content_001",
+    "decision_id": "d_001",
+    "allow_walk": false,
+    "walk_gate_snapshot": {}
+  }
+}
+```
+
+`hashtag_to_query` skip action 建议:
+
+```json
+{
+  "edge_id": "hashtag_to_query",
+  "walk_status": "skipped",
+  "reason_code": "v4_allow_walk_denied",
+  "raw_payload": {
+    "hashtag": "父爱感悟",
+    "source_content_id": "content_001",
+    "decision_id": "d_001",
+    "allow_walk": false,
+    "walk_gate_snapshot": {}
+  }
+}
+```
+
+`author_to_works` skip action 建议:
+
+```json
+{
+  "edge_id": "author_to_works",
+  "walk_status": "skipped",
+  "reason_code": "v4_allow_walk_denied",
+  "raw_payload": {
+    "platform_author_id": "author_001",
+    "decision_id": "d_001",
+    "allow_walk": false,
+    "walk_gate_snapshot": {}
+  }
+}
+```
+
+## 实施步骤
+
+### query_next_page
+
+1. 在 `_expand_queries(...)` 中,保留 `edge_supported(profile, "query_next_page")` 的外层判断。
+2. 保留 `has_more`、`next_cursor`、`seen_queries`、`source query` 存在检查。
+3. 在 `_query_effect_by_search_query_id(...)` 返回值中补充 query 下的候选 decision 或 allow_walk 聚合信息。
+4. 修改 `_can_fetch_next_page(...)` 或新增 `_can_fetch_next_page_for_item(...)`:
+   - query effect 必须为 `success`。
+   - 触发该 query 的内容 decision 必须是 V4 `allow_walk=true`,或非 V4 走旧 success 规则。
+5. 当 query effect success 但 V4 `allow_walk=false`:
+   - 不生成 page query。
+   - 写 `query_next_page` skip action。
+   - `reason_code="v4_allow_walk_denied"`。
+   - raw payload 写 parent query、source content、decision gate context。
+6. 保留预算耗尽逻辑:合格候选超过 `page_budget` 时仍写 `budget_exhausted`。
+
+### hashtag_to_query
+
+1. 保留 profile 支持判断:`video_to_hashtag` 与 `hashtag_to_query` 都 supported 才处理。
+2. 对每个 item 先取 decision。
+3. 调 `_edge_permission_for(decision, "video_to_hashtag", policy)`。
+4. 若 V4 allow false 导致 deny:
+   - item 有 tags 时写 skip action。
+   - `reason_code="v4_allow_walk_denied"`。
+   - 不进入 tag 生成循环。
+5. 若 review / reject / non-V4 deny:
+   - 保持现有 `review_tag_expansion_disabled` / `blocked_by_rule_decision` 行为。
+6. allow 时保留现有 tag 去重、blocked tag、budget exhausted。
+
+### author_to_works
+
+1. 保留 profile blocked 优先:
+   - `edge_supported(profile, "author_to_works")` false 时显式 skip `edge_blocked_by_platform_profile`。
+   - 不调用平台接口。
+2. 保留平台 client 能力检查:无 `fetch_author_works/author_works` 时不执行。
+3. 对每个 author item 取对应 decision。
+4. 调 `_edge_permission_for(decision, "author_to_works", policy)`。
+5. 若 V4 allow false 导致 deny:
+   - 写 `author_to_works` skip action。
+   - `reason_code="v4_allow_walk_denied"`。
+   - 不调用 `fetch_author_works`。
+6. allow 时保留现有:
+   - `low_budget` 语义。
+   - try/except failure action。
+   - 作者作品去重。
+   - 合成 `author_works` query 行。
+   - 新作品进入 content discovery + pattern recall + rule judgment。
+
+## 验证命令
+
+```bash
+uv run pytest tests/test_walk_engine_pagination.py tests/test_walk_engine_tag.py tests/test_walk_engine_author.py -q
+uv run pytest tests/test_walk_profile_degradation.py tests/test_case_replay.py -q
+```
+
+## Unit Test
+
+- pagination:
+  - V4 success query 下只有 `allow_walk=false` 内容,不生成 page query。
+  - 同一 query 下存在 `allow_walk=true` 内容,生成 page query。
+  - non-V4 success query 仍按旧规则生成 page query。
+  - 预算耗尽仍写 `budget_exhausted`。
+- tag:
+  - V4 allow false + tags 不生成 tag query,写 `v4_allow_walk_denied`。
+  - V4 allow true + tags 生成 tag query。
+  - blocked tag 不生成 query,且不误报 allow_walk deny。
+  - 预算耗尽仍写 `budget_exhausted`。
+- author:
+  - V4 allow false 不调用 fake client。
+  - V4 allow true 调用 fake client。
+  - profile blocked 不调用 fake client,reason 仍是 `edge_blocked_by_platform_profile`。
+  - author works duplicate 仍跳过已发现内容。
+
+## Integrated Test
+
+- 在 `tests/test_v4_m4_walk_replay.py` 中构造:
+  - 内容 A:`allow_walk=true`,有 cursor/tag/author。
+  - 内容 B:`allow_walk=false`,有 cursor/tag/author。
+- 运行 `run_bounded_walk(...)`。
+- 断言:
+  - 只有内容 A 的 tag/page/author 扩展成功。
+  - 内容 B 的扩展全部为 skip 或无 query row。
+  - fake author client 只收到内容 A 的 author。
+  - runtime `validate_run()` pass。
+
+## 失败归因
+
+- page query 仍生成:先查 `_can_fetch_next_page` 是否只看了 query effect。
+- tag query 仍生成:先查 `_edge_permission_for` 是否没有识别 V4。
+- author client 被调用:先查 `_expand_authors` 是否在 permission 前调用了 client。
+- profile blocked 被覆盖:先查 blocked 判断是否在 allow_walk 判断之前。
+- 预算测试失败:先查 `budget_exhausted` 分支是否被 allow_walk deny 抢先。
+
+## sub-agent 交叉验证要点
+
+- 代码线:确认 M4B 覆盖当前三个实际扩展入口。
+- JSON/config 线:确认三平台 edge status 不变。
+- DB/runtime 线:确认 page/tag/author skip action raw payload 足够回放。

+ 178 - 0
tech_documents/工程落地/v4_implementation_briefs/M4/M4C_Walk_Policy_JSON_Excel_Validator_Contract.md

@@ -0,0 +1,178 @@
+# M4C Walk Policy JSON Excel Validator Contract Implementation Brief
+
+状态:本 brief 覆盖 M4 的配置合同。M4C 只规划 walk policy / 游走策略 Excel / validator 对 `allow_walk` 消费的表达,不改 M3 rule pack 主 scorecard,不新增 config gate 入口。
+
+## 目标
+
+让 M4 allow_walk gate 不只是代码约定,而是可被配置和 gate 校验:
+
+1. `walk_policy.json` 明确 V4 出边需要 `allow_walk=true`。
+2. 游走策略 Excel 与 JSON 保持 byte-equal / sync。
+3. `validate_v4_config_contract.py` 能发现 M4 gate 缺失或字段漂移。
+4. `run_config_gate.py` 继续通过现有 `v4_config_contract` 入口覆盖 M4。
+5. 不重复修改 rule pack 主 JSON/Excel 的 M3 scorecard 口径。
+
+## 现有证据
+
+- `product_documents/规则包/douyin_rule_packs.v1.json` 已是 `strategy_version=V4`,rule pack version 为 `4.0.0`。
+- 规则包 Excel `input_contract_fields` 已含 `pattern_match_result.query_relevance_score`、`content_engagement_metrics.platform_performance.platform_performance_score`、`missing_observable_fields`。
+- `tech_documents/数据接口与来源/walk_policy.json` 当前包含 `global`、`edge_budgets`、`dedup`、`edge_permissions`,但没有 `v4_walk_gate`。
+- `product_documents/抖音游走策略/douyin_walk_strategy.v1.json` 当前只包含 `walk_edge_catalog`、`walk_rule_pack_binding`、`walk_fact_contract`。
+- 游走策略 Excel 当前同样只有 `walk_edge_catalog`、`walk_rule_pack_binding`、`walk_fact_contract` 三个业务 sheet。
+- `scripts/validate_v4_config_contract.py` 当前校验 walk policy 必备 section,但不校验 `allow_walk` gate。
+- `scripts/run_config_gate.py` 已包含 `v4_config_contract`,不需要新增入口。
+
+## 修改范围
+
+- 后续实现中可修改:
+  - `tech_documents/数据接口与来源/walk_policy.json`
+  - `product_documents/抖音游走策略/douyin_walk_strategy.v1.json`
+  - `tech_documents/游走策略/游走策略配置表.xlsx`
+  - `scripts/validate_v4_config_contract.py`
+  - `tests/test_v4_walk_contract.py`
+  - `tests/test_walk_strategy_config.py`
+- 后续实现中可选择:
+  - 新增游走 Excel sheet:`v4_walk_gate`
+  - 或在现有 `walk_fact_contract` / JSON notes 中新增可同步字段
+
+## 不修改范围
+
+- 不修改 `product_documents/规则包/douyin_rule_packs.v1.json` 的 V4 scorecard 维度。
+- 不修改 `tech_documents/规则包映射/规则包映射配置表.xlsx` 的 M3 scorecard sheet,除非 validator 发现 M4 必需字段缺失。
+- 不新增 `run_config_gate.py` gate 名称。
+- 不改平台 profile edge status 语义:运行层只接受 `supported/blocked`。
+- 不把 author portrait threshold 或 tag LLM threshold 强行落地为 M4 必做功能。
+
+## 涉及文件 / 函数 / 类
+
+- `tech_documents/数据接口与来源/walk_policy.json`
+- `product_documents/抖音游走策略/douyin_walk_strategy.v1.json`
+- `tech_documents/游走策略/游走策略配置表.xlsx`
+- `scripts/validate_v4_config_contract.py`
+  - `validate_v4_config_contract(...)`
+  - walk policy 检查相关 helper
+  - platform profile 检查相关 helper
+- `scripts/build_config_from_excel.py`
+- `scripts/validate_config_excel_sync.py`
+- `scripts/run_config_gate.py`
+- `tests/test_v4_walk_contract.py`
+- `tests/test_walk_strategy_config.py`
+
+## 数据合同
+
+`walk_policy.json` 新增建议:
+
+```json
+{
+  "v4_walk_gate": {
+    "requires_allow_walk": true,
+    "source_field": "rule_decisions.jsonl[].decision_replay_data.allow_walk",
+    "deny_reason_code": "v4_allow_walk_denied",
+    "applies_to_edges": [
+      "query_next_page",
+      "hashtag_to_query",
+      "author_to_works"
+    ],
+    "raw_payload_fields": [
+      "decision_id",
+      "allow_walk",
+      "allow_walk_reason",
+      "walk_gate_snapshot"
+    ]
+  }
+}
+```
+
+游走策略 JSON / Excel 可增加同义结构:
+
+```json
+{
+  "walk_fact_contract": [
+    {
+      "runtime_file": "walk_actions.jsonl",
+      "json_fields": ["raw_payload"],
+      "notes": "M4 requires allow_walk consumption evidence in raw_payload."
+    }
+  ],
+  "v4_walk_gate": [
+    {
+      "gate_id": "allow_walk_required",
+      "applies_to_edge_id": "query_next_page",
+      "required_field": "decision_replay_data.allow_walk",
+      "required_value": true,
+      "deny_reason_code": "v4_allow_walk_denied"
+    }
+  ]
+}
+```
+
+## 实施步骤
+
+1. 在 `walk_policy.json` 新增 `v4_walk_gate`:
+   - `requires_allow_walk=true`。
+   - `deny_reason_code=v4_allow_walk_denied`。
+   - `applies_to_edges` 包含 `query_next_page/hashtag_to_query/author_to_works`。
+   - `raw_payload_fields` 包含 M4 必需消费证据字段。
+2. 评估游走策略 JSON/Excel:
+   - 若 Excel 是人维护真相源,则新增 `v4_walk_gate` sheet。
+   - 同步生成 `product_documents/抖音游走策略/douyin_walk_strategy.v1.json`。
+   - 保持 `build_config_from_excel.py --check` byte-equal。
+3. 增强 `validate_v4_config_contract.py`:
+   - 校验 `walk_policy.v4_walk_gate` 存在。
+   - 校验 `requires_allow_walk is true`。
+   - 校验 `deny_reason_code == "v4_allow_walk_denied"`。
+   - 校验 `applies_to_edges` 覆盖三条边。
+   - 校验 raw payload fields 覆盖 `decision_id/allow_walk/allow_walk_reason/walk_gate_snapshot`。
+4. 校验 `walk_graph.json` 一致性:
+   - `query_next_page`、`hashtag_to_query`、`author_to_works` 必须在 walk graph edges 中存在。
+   - `hashtag_to_query` 与 `author_to_works` 已是 decision/reseed gated;`query_next_page` 虽是 navigate,也要通过 M4 gate 配置覆盖。
+5. 保持三平台 profile 校验:
+   - edge status 只允许 `supported/blocked`。
+   - 抖音 page/author/tag supported。
+   - 快手 page/author blocked、tag supported。
+   - 视频号 page/tag supported、author blocked。
+6. 保持 `scripts/run_config_gate.py` 入口不变,只让 `v4_config_contract` 变强。
+
+## 验证命令
+
+```bash
+uv run python scripts/validate_v4_config_contract.py
+uv run --with openpyxl python scripts/build_config_from_excel.py --check
+uv run --with openpyxl python scripts/validate_config_excel_sync.py
+python3 scripts/run_config_gate.py
+uv run pytest tests/test_v4_walk_contract.py tests/test_walk_strategy_config.py -q
+```
+
+## Unit Test
+
+- `tests/test_v4_walk_contract.py`
+  - `walk_policy.v4_walk_gate.requires_allow_walk is true`。
+  - `deny_reason_code == "v4_allow_walk_denied"`。
+  - `applies_to_edges` 包含三条 M4 扩展边。
+  - `raw_payload_fields` 包含四个必需字段。
+  - 三平台 profile 的 page/author/tag status 符合 M4 计划。
+- `tests/test_walk_strategy_config.py`
+  - 游走策略 JSON 可加载。
+  - Excel / JSON byte-equal。
+  - 若新增 `v4_walk_gate` sheet,required section 更新且 validator 通过。
+
+## Integrated Test
+
+- `run_config_gate.py` 全量 pass。
+- `build_config_from_excel.py --check` pass。
+- `validate_config_excel_sync.py` pass。
+- 与 M4D 的 runtime replay 一起证明配置字段被代码消费。
+
+## 失败归因
+
+- config gate 未发现缺失:先查 `validate_v4_config_contract.py` 是否真的读取 `walk_policy.json`。
+- Excel byte drift:先查新增 sheet 是否被 `build_config_from_excel.py` 纳入 canonical build。
+- `run_config_gate.py` 没覆盖:先查 `v4_config_contract` 是否仍在 gate 列表。
+- profile status 报错:先查三平台 profile 是否仍有 `weak/unsupported/partial` 写在 `status`。
+- rule pack JSON 被误改:先查是否把 M4 gate 错写到规则包 scorecard sheet。
+
+## sub-agent 交叉验证要点
+
+- JSON/config 线:确认 M4C 的重点是 walk policy,不是主 rule pack。
+- 代码线:确认 `walk_engine` 能读取配置或至少与配置常量一致。
+- DB/runtime 线:确认配置字段最终进入 runtime raw payload 的消费证据。

+ 185 - 0
tech_documents/工程落地/v4_implementation_briefs/M4/M4D_Runtime_Validation_DB_And_Source_Path_Replay_Contract.md

@@ -0,0 +1,185 @@
+# M4D Runtime Validation DB And Source Path Replay Contract Implementation Brief
+
+状态:本 brief 覆盖 M4 的 runtime validator、DB JSON 容器和 source path / walk action 可回放合同。M4D 不新增 DB 表、不新增正式列,只增强跨文件校验与 fake DB replay。
+
+## 目标
+
+让 M4 runtime 能证明每条扩展 action 都遵守 V4 `allow_walk`:
+
+1. 成功的 `query_next_page/hashtag_to_query/author_to_works` 必须追溯到 `allow_walk=true`。
+2. `allow_walk=false` 的 V4 decision 不得产生成功扩展 action。
+3. V4 walk action raw payload 必须包含 gate 消费证据。
+4. source path 与 walk action id 继续闭合。
+5. DB runtime fake 写入后 JSON 容器不丢字段。
+
+## 现有证据
+
+- `content_agent/integrations/runtime_files.py::RUNTIME_FILENAMES` 已包含 `walk_actions.jsonl`、`source_path_records.jsonl`、`search_clues.jsonl`、`rule_decisions.jsonl`。
+- `content_agent/integrations/database_runtime.py::RUNTIME_FILE_TABLES` 已映射 `walk_actions.jsonl` 到 `content_agent_walk_actions`。
+- `database_runtime.JSON_COLUMNS_BY_TABLE` 已包含 `content_agent_walk_actions.raw_payload`、`content_agent_source_path_records.raw_payload`、`content_agent_search_clues.raw_payload`、`content_agent_rule_decisions.decision_replay_data`。
+- `run_record/validation.py::_check_v4_walk_gate_contract(...)` 当前只校验 decision producer,不校验 walk actions。
+- `run_record/validation.py::_check_references(...)` 已读取 walk action ids 与 source path raw payload 中的 `walk_action_id`。
+- `tests/test_database_runtime.py` 已覆盖 V4 scorecard / decision replay JSON 落库。
+- `tests/test_v4_m3_scoring_replay.py` 已覆盖 M3 scoring replay 与 DB fake 容器保留。
+- DB/runtime subagent 只读审计确认 DB 不需要 migration;但本轮 `validate_content_agent_db.py --allow-missing-tables` 因 MySQL 2013 断线未完成。
+
+## 修改范围
+
+- 后续实现中可修改:
+  - `content_agent/business_modules/run_record/validation.py`
+  - `content_agent/integrations/database_runtime.py`(仅当 serializer 缺字段,当前预期不需要)
+  - `tests/test_v4_validator_contract.py`
+  - `tests/test_database_runtime.py`
+  - `tests/test_v4_m4_walk_replay.py`
+- 后续实现中可新增 validator helper:
+  - `_check_v4_walk_action_consumption(...)`
+  - `_v4_decision_by_id(...)`
+  - `_v4_decision_by_content_id(...)`
+  - `_walk_action_decision_refs(...)`
+
+## 不修改范围
+
+- 不新增 DB migration。
+- 不新增 `allow_walk` 正式列。
+- 不要求 `validate_run(...)` 直接连 DB。
+- 不改变 existing raw payload forbidden key 规则。
+- 不改变 source path 三类主合同:`pattern_to_search_query`、`search_query_to_content`、`decision_to_asset`。
+
+## 涉及文件 / 函数 / 类
+
+- `content_agent/business_modules/run_record/validation.py`
+  - `validate_run(...)`
+  - `_load_files(...)`
+  - `_check_references(...)`
+  - `_check_source_paths(...)`
+  - `_check_v4_walk_gate_contract(...)`
+  - `_check_v4_action_thresholds(...)`
+  - 新增 `_check_v4_walk_action_consumption(...)`
+- `content_agent/integrations/database_runtime.py`
+  - `RUNTIME_FILE_TABLES`
+  - `JSON_COLUMNS_BY_TABLE`
+  - `_record_for_jsonl(...)`
+  - `_runtime_payload(...)`
+- `tests/test_v4_validator_contract.py`
+- `tests/test_database_runtime.py`
+- `tests/test_v4_m4_walk_replay.py`
+
+## 数据合同
+
+V4 walk action 成功扩展必须包含:
+
+```json
+{
+  "edge_id": "author_to_works",
+  "walk_status": "success",
+  "raw_payload": {
+    "decision_id": "d_001",
+    "decision_action": "ADD_TO_CONTENT_POOL",
+    "allow_walk": true,
+    "allow_walk_reason": "query>=70/platform>=65/score>=70",
+    "walk_gate_snapshot": {
+      "query_relevance_score": 80,
+      "platform_performance_score": 70,
+      "score": 75
+    }
+  }
+}
+```
+
+V4 allow false skip 必须包含:
+
+```json
+{
+  "edge_id": "hashtag_to_query",
+  "walk_status": "skipped",
+  "reason_code": "v4_allow_walk_denied",
+  "raw_payload": {
+    "decision_id": "d_002",
+    "allow_walk": false,
+    "walk_gate_snapshot": {
+      "query_relevance_score": 80,
+      "platform_performance_score": 60,
+      "score": 70
+    }
+  }
+}
+```
+
+## 实施步骤
+
+1. 在 `validation.py` 新增 V4 walk consumer 校验入口,并在 `validate_run(...)` 主流程中调用。
+2. 构建 V4 decision 索引:
+   - by `decision_id`。
+   - by `decision_target_id` / platform content id。
+3. 定义扩展边集合:
+   - `query_next_page`
+   - `hashtag_to_query`
+   - `author_to_works`
+4. 对每条 V4 扩展成功 action:
+   - 从 `raw_payload.decision_id` 或 source content id 找到 decision。
+   - decision 必须是 V4。
+   - `decision_replay_data.allow_walk` 必须为 true。
+   - action raw payload 必须包含 `allow_walk=true` 与 `walk_gate_snapshot`。
+5. 对每条 `reason_code=v4_allow_walk_denied` 的 skip action:
+   - raw payload 必须包含 `decision_id`。
+   - raw payload `allow_walk` 必须为 false。
+   - raw payload 必须包含 `walk_gate_snapshot`。
+6. 对 V4 `allow_walk=false` decision:
+   - 不允许存在以该 decision/content/query 为来源的扩展 success action。
+   - 允许 `path_stop`、`budget_downgrade`、`skipped`。
+7. 保持现有 `source_path_records.raw_payload.walk_action_id` 引用校验。
+8. DB fake 测试写入:
+   - `rule_decisions.jsonl`
+   - `walk_actions.jsonl`
+   - `source_path_records.jsonl`
+   - `search_clues.jsonl`
+   - `search_queries.jsonl`
+9. 确认 `DatabaseRuntimeStore` 不过滤 M4 gate context。
+
+## 验证命令
+
+```bash
+uv run pytest tests/test_v4_validator_contract.py tests/test_v4_m4_walk_replay.py -q
+uv run pytest tests/test_database_runtime.py tests/test_walk_actions_runtime.py -q
+uv run python scripts/validate_content_agent_db.py --allow-missing-tables
+uv run python scripts/validate_schema_registry.py
+```
+
+## Unit Test
+
+- validator success:
+  - V4 allow true + author success action + raw payload gate context → pass。
+  - V4 allow false + skip action `v4_allow_walk_denied` + raw payload gate context → pass。
+- validator failure:
+  - V4 allow false + `author_to_works` success → fail。
+  - V4 allow false + `hashtag_to_query` success → fail。
+  - V4 allow false + `query_next_page` success → fail。
+  - V4 walk action 缺 `walk_gate_snapshot` → fail。
+  - V4 walk action 缺 `decision_id` 且无法从 content/query 反查 → fail。
+- DB fake:
+  - `walk_actions.raw_payload.allow_walk` 保留。
+  - `walk_actions.raw_payload.walk_gate_snapshot` 保留。
+  - `source_path_records.raw_payload.walk_action_id` 保留。
+
+## Integrated Test
+
+- `tests/test_v4_m4_walk_replay.py`:
+  - 运行 `run_bounded_walk(...)`。
+  - 写 local runtime。
+  - `validate_run(...)` pass。
+  - 使用 fake DB connection 写入 runtime rows。
+  - 断言 DB insert values 中 raw payload 保留 M4 gate context。
+
+## 失败归因
+
+- validator 找不到 decision:先查 walk action raw payload 是否写 `decision_id`。
+- page action 关联不到 content:先查 page skip/action raw payload 是否写 `source_content_id` 或 parent query 与 query-source 映射。
+- DB fake 丢字段:先查 `with_raw_payload(...)` 是否在构造 action 前包含字段。
+- source path 断链:先查 `source_path_records.raw_payload.walk_action_id` 是否引用真实 `walk_actions.walk_action_id`。
+- schema validator 失败:先查是否误新增 DB 列但未更新 schema registry。
+
+## sub-agent 交叉验证要点
+
+- DB/runtime 线:确认不需要 migration,JSON 容器足够。
+- 代码线:确认 runtime validator 补的是 consumer 合同,不重算 M3 分数。
+- JSON/config 线:确认 validator 的 expected reason code 与 config 中 `v4_allow_walk_denied` 一致。

+ 163 - 0
tech_documents/工程落地/v4_implementation_briefs/M4/M4E_Acceptance_Commands_And_Subagent_Audit.md

@@ -0,0 +1,163 @@
+# M4E Acceptance Commands And Subagent Audit Implementation Brief
+
+状态:本 brief 覆盖 M4 的验收命令、测试矩阵、DB 只读验证和 subagent 审计落点。M4E 不定义新功能,只把 M4A-M4D 的实施验收收口。
+
+## 目标
+
+用可重复命令证明 M4 的 allow_walk 消费已落地:
+
+1. Unit Test 覆盖 author/tag/page 三条边。
+2. Integrated Test 覆盖 V4 allow true/false replay。
+3. Runtime validator 能抓到 allow false 的错误扩展。
+4. DB fake 能证明 JSON 容器保留消费证据。
+5. Config gate 能证明 walk policy/Excel/validator 同步。
+6. 真实 DB 只读 validator 在验收时必须重跑并记录结果。
+
+## 现有证据
+
+- 旧 subagent 关闭请求返回 `not found`,说明本轮无可复用旧审计线。
+- 新代码线 subagent Bohr 确认:M3 已产 `allow_walk`,M4 `walk_engine` 未消费。
+- 新 JSON/config 线 subagent Newton 确认:rule pack 已 V4,但 walk policy/Excel 缺 M4 gate。
+- 新 DB/runtime 线 subagent Nietzsche 确认:DB 不需要 migration,runtime JSON 容器足够。
+- 本地/DB 线只读验证:`validate_schema_registry.py` pass。
+- DB 线只读验证:`tests/test_database_runtime.py tests/test_v4_m3_scoring_replay.py tests/test_walk_actions_runtime.py` 共 37 个测试 pass。
+- DB 只读 validator 本轮失败原因是 MySQL `2013 Lost connection`,不是 schema finding;验收必须重跑。
+
+## 修改范围
+
+- 后续实现中新增或修改测试:
+  - `tests/test_walk_engine_author.py`
+  - `tests/test_walk_engine_tag.py`
+  - `tests/test_walk_engine_pagination.py`
+  - `tests/test_v4_walk_contract.py`
+  - `tests/test_v4_validator_contract.py`
+  - `tests/test_v4_m4_walk_replay.py`
+  - `tests/test_database_runtime.py`
+  - `tests/test_case_replay.py`
+  - `tests/test_walk_profile_degradation.py`
+- 后续实现中不需要新增独立 gate command,只增强现有 validator。
+
+## 不修改范围
+
+- 不把本轮 DB 断线写成通过。
+- 不删除既有 M2/M3 replay。
+- 不用 M4 replay 代替 full `uv run pytest -q`。
+- 不把 `.claude/`、`e2e_logs/` 等本地目录纳入验收产物。
+
+## 涉及文件 / 函数 / 类
+
+- `tests/test_v4_m4_walk_replay.py`
+- `tests/test_v4_walk_contract.py`
+- `tests/test_v4_validator_contract.py`
+- `tests/test_walk_engine_author.py`
+- `tests/test_walk_engine_tag.py`
+- `tests/test_walk_engine_pagination.py`
+- `tests/p6_walk_helpers.py`
+- `tests/test_database_runtime.py`
+- `scripts/validate_content_agent_db.py`
+- `scripts/validate_schema_registry.py`
+- `scripts/validate_v4_config_contract.py`
+- `scripts/run_config_gate.py`
+
+## 验收测试矩阵
+
+| 场景 | 输入 | 预期 |
+|---|---|---|
+| V4 allow true author | `ADD_TO_CONTENT_POOL + allow_walk=true` | 调用 `fetch_author_works`,成功 action 有 gate context |
+| V4 allow false author | `ADD_TO_CONTENT_POOL + allow_walk=false` | 不调用平台,skip `v4_allow_walk_denied` |
+| V4 allow true tag | 内容有 tag | 生成 tag query |
+| V4 allow false tag | 内容有 tag | 不生成 tag query,skip `v4_allow_walk_denied` |
+| V4 allow true page | `has_more=true + cursor` | 生成 page query |
+| V4 allow false page | `has_more=true + cursor` | 不生成 page query,skip `v4_allow_walk_denied` |
+| profile blocked | 快手/视频号 author blocked | skip `edge_blocked_by_platform_profile`,不调用平台 |
+| budget exhausted | allow true 但超预算 | skip `budget_exhausted` |
+| non-V4 decision | 无 V4 scorecard | 旧 edge permission 行为不变 |
+| validator negative | allow false 但 action success | `validate_run` fail |
+
+## 验证命令
+
+必须执行:
+
+```bash
+uv run pytest tests/test_walk_engine_author.py tests/test_walk_engine_tag.py tests/test_walk_engine_pagination.py -q
+uv run pytest tests/test_v4_walk_contract.py tests/test_v4_validator_contract.py tests/test_v4_m4_walk_replay.py -q
+uv run pytest tests/test_database_runtime.py tests/test_v4_m3_scoring_replay.py tests/test_walk_actions_runtime.py -q
+uv run pytest tests/test_case_replay.py tests/test_walk_profile_degradation.py -q
+uv run python scripts/validate_schema_registry.py
+uv run python scripts/validate_rule_pack_config.py
+uv run python scripts/validate_v4_config_contract.py
+uv run --with openpyxl python scripts/build_config_from_excel.py --check
+uv run --with openpyxl python scripts/validate_config_excel_sync.py
+python3 scripts/run_config_gate.py
+uv run pytest -q
+```
+
+必须重跑且记录结果:
+
+```bash
+uv run python scripts/validate_content_agent_db.py --allow-missing-tables
+```
+
+如果 MySQL 再次返回连接异常,验收记录必须区分:
+
+- schema finding failure:阻塞。
+- 连接/网络异常:记录原始错误,并在可连接环境重跑。
+
+## Unit Test
+
+- author 单测:
+  - allow false 不调用 fake author client。
+  - allow true 调用 fake author client。
+  - non-V4 保持旧行为。
+- tag 单测:
+  - allow false 不生成 query。
+  - allow true 生成 query。
+  - budget/profile/blocked tag 行为不变。
+- pagination 单测:
+  - allow false 不生成 page query。
+  - allow true 生成 page query。
+- validator 单测:
+  - 错误成功扩展 fail。
+  - raw payload 缺 gate context fail。
+  - 正确 skip pass。
+
+## Integrated Test
+
+- `tests/test_v4_m4_walk_replay.py`:
+  - 使用 fake platform client 统计 `search` / `fetch_author_works` 调用。
+  - 用两条 V4 decision 构造 allow true/false 对照。
+  - 运行 `run_bounded_walk(...)`。
+  - 写 local runtime 并 `validate_run(...)`。
+  - 用 fake DB connection 写入关键 runtime 文件。
+  - 断言 DB raw payload 保留 `allow_walk` 消费证据。
+
+## 失败归因
+
+- Unit pass 但 integration fail:先查 helper fixture 是否写了完整 `decision_replay_data`。
+- Integration pass 但 validator fail:先查 walk action raw payload 是否缺 `decision_id/walk_gate_snapshot`。
+- Config gate fail:先查 Excel/JSON 是否 byte-equal。
+- Full pytest fail in old walk tests:先查 non-V4 fallback 是否被破坏。
+- DB validator fail:先区分 MySQL 连接异常与 schema finding。
+
+## sub-agent 审计结论
+
+- 代码线 `019ec71d-079d-7f81-821e-ff9b9cac4498`:
+  - `walk_engine._edge_permission_for` 未消费 `allow_walk`。
+  - `query_next_page` 是最大缺口,因为它只看 query effect success。
+  - 推荐 unit 覆盖 author/tag/page 三边。
+- JSON/config 线 `019ec71d-2a9c-7f61-af9c-3963a56afe8a`:
+  - M3 rule pack 已 V4,不需要为 M4 改 scorecard。
+  - walk policy / 游走 Excel 缺 `allow_walk` gate。
+  - `validate_v4_config_contract.py` 需要补强。
+- DB/runtime 线 `019ec71d-4904-7212-8d45-2d9e0504aa13`:
+  - DB 不需要 migration。
+  - `walk_actions.raw_payload`、`source_path_records.raw_payload`、`search_clues.raw_payload` 足够承载 M4。
+  - `validate_content_agent_db.py` 本轮 MySQL 2013 断线,验收必须重跑。
+
+## 最终通过标准
+
+- M4 6 个 Markdown brief 全部存在。
+- 实现阶段所有必跑命令通过,或 DB 连接异常被单独记录。
+- `allow_walk=false` 不再产生任何 V4 扩展 success action。
+- `allow_walk=true` 的既有 page/tag/author 成功路径不回退。
+- `walk_actions.raw_payload` 可以解释每条 V4 扩展 action 的 gate 结果。