zizi b0bc7a8745 框架: 技能按动作-对象重组 + 先审后入创作闭环
一、技能重组(动作-对象命名)
- 旧目录 clean/confirm/continuation/db/detect/embed/… 重组为
  clean-book-text/decide-candidate/write-next-chapter/access-database/
  check-content-consistency/embed-knowledge/…(git 识别为 rename,内容保持)
- agents/*.md、AGENTS.md/CLAUDE.md 收编、example_skill 登记表同步新名

二、先审后入创作闭环(本次核心)
正文接受从"机械门一过就写正典"改为"机械门+语义审查双通过+用户批准+单事务原子提交",
DB 级兜底,编排层跳步即被硬拒。
- candidate_cas.py + example_candidate_cas(109):持久化 CAS 状态链
- fact_delta.py + example_fact_delta/example_fact_ledger(106):结构化事实增量,
  模型只提六型闭集增量+正文证据引文,仅用户批准的增量随正文同事务入账本
- projection_registry.py + example_projection_run(107):投影登记与恢复
- acceptance_state.py:接受前置实时状态重读
- lesson_registry.py + example_lesson(108):经验升格链,禁止自动升格
- DDL 105:example_candidate 增 semantic_status/semantic_report_sha256
- write_canonical.accept:语义兜底+同事务合并增量+登记投影;
  run_writer_pipeline/persist_writer_run/run_writer_semantic_detector/step2 接入全链
- claude_runtime:兼容新 CLI modelUsage 信息字段

三、审查修复(独立子代理四维审查后)
- 事实增量 propose→approve 翻态正道,不撞唯一键
- 冻结配置探针重刷(CLI 2.1.211→2.1.231 漂移),profileSha256/adapterVersion 再登记
- 可视化合同悬空路径/五六空间矛盾、 SoT 旧技能名漂移、行尾空白清理

测试:离线 65 套 + 真实库集成 5 套(CAS/接受故障注入/事实增量/投影/经验升格)+ 回放 79 项全绿。
创作内容(docs/design、生成正文 artifacts)按"框架与创作分开"未入本提交。
2026-08-14 10:24:08 +08:00

73 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""文风取数端回归:load_confirmed_style / derive_style_constraints(桩连接,不连真库)。
read-context 的文风取数端,与细纲/范式取数端并排成一等公民。优先独立 style 规划行,
回退设定行的 style 字段(书12 现状为一句话 blob)。结构化画像抽取仍待建。
"""
import pathlib
import sys
import unittest
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
from retrieve_writer_sources import derive_style_constraints, load_confirmed_style # noqa: E402
class FakeCursor:
def __init__(self, row):
self._row = row
def fetchone(self):
return self._row
class FakeConn:
"""按 SQL 区分两次查询:schema_type='style' 的独立文风行 / section_type='setting' 的设定行。"""
def __init__(self, style_row=None, setting_row=None):
self.style_row = style_row
self.setting_row = setting_row
def execute(self, sql, params=None):
if "schema_type='style'" in sql:
return FakeCursor(self.style_row)
if "section_type='setting'" in sql:
return FakeCursor(self.setting_row)
return FakeCursor(None)
class DeriveStyleTest(unittest.TestCase):
def test_blob_becomes_single_constraint(self):
self.assertEqual(derive_style_constraints("冷峻、克制、微恐。"), ["冷峻、克制、微恐。"])
def test_structured_dict_projects_per_aspect(self):
self.assertEqual(
derive_style_constraints({"叙事人称": "第三人称限制视角", "AI味黑名单": "禁 仿佛/似乎", "达标样张": ""}),
["叙事人称:第三人称限制视角", "AI味黑名单:禁 仿佛/似乎"],
)
def test_blank_and_illegal_return_empty(self):
self.assertEqual(derive_style_constraints(" "), [])
self.assertEqual(derive_style_constraints(None), [])
self.assertEqual(derive_style_constraints(42), [])
class LoadConfirmedStyleTest(unittest.TestCase):
def test_prefers_dedicated_style_section(self):
conn = FakeConn(style_row=('{"叙事人称": "第三人称"}',),
setting_row=('{"style": "不该用到我"}',))
self.assertEqual(load_confirmed_style(conn, work_id=12), ["叙事人称:第三人称"])
def test_falls_back_to_setting_style_field(self):
conn = FakeConn(style_row=None, setting_row=('{"title": "深渊机神", "style": "冷峻克制"}',))
self.assertEqual(load_confirmed_style(conn, work_id=12), ["冷峻克制"])
def test_no_confirmed_style_returns_empty(self):
conn = FakeConn(style_row=None, setting_row=None)
self.assertEqual(load_confirmed_style(conn, work_id=12), [])
if __name__ == "__main__":
unittest.main()