一、技能重组(动作-对象命名) - 旧目录 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)按"框架与创作分开"未入本提交。
129 lines
7.0 KiB
Python
129 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
||
"""A3:23 型结构本体 YAML → meta 表行(W1 种子演练,幂等可重跑)。
|
||
|
||
映射约定(详见 db/表映射.md 与本次收口记录):
|
||
- muse_meta_schema 一型一行;schema_key=target_type;状态 启用→active / 待启用→inactive
|
||
- muse_meta_schema_version 每型 v1,active_flag=TRUE;field_contract_snapshot=完整 YAML(判据/说明/设计发现全在)
|
||
- muse_meta_field 规范化投影行:基础字段 sort_order 1–9,特有字段 11 起(段位即基础/特有约定)
|
||
- muse_meta_visibility_policy 版本级一行:ai_context=是否存在 AI 可见字段;
|
||
字段级细则进 policy_snapshot.fieldAiContext(主仓无字段级列——设计发现,待回填)
|
||
- 幂等:schema/version/policy upsert;field 行重跑=删旧插新(meta 种子行豁免软删约定,见表映射.md)
|
||
"""
|
||
import json
|
||
import pathlib
|
||
import sys
|
||
|
||
import psycopg
|
||
import yaml
|
||
from psycopg.types.json import Jsonb
|
||
|
||
DSN = "postgresql://root:f6710e2d0294eb1c10e26a805a64bc54@100.64.0.8:5433/muse-example"
|
||
SCHEMA_DIR = pathlib.Path(__file__).resolve().parents[4] / "meta" / "schemas"
|
||
TENANT, ACTOR = 1, "1" # 实验写入约定:系统主账号
|
||
|
||
# 基础字段(所有型共有,README §使用规则;aiContext 为实验设计判断,可在 B4 优化环调整):
|
||
# 名称/别名/摘要/标签全用途可见;来源(出处)生成不可见防抄袭腔;状态是系统轨道值不进上下文(授权过滤在查询层)
|
||
BASE_FIELDS = [
|
||
("名称", "text", True, None, True),
|
||
("别名", "array", False, None, True),
|
||
("一句话摘要", "text", True, None, True),
|
||
("标签", "array", False, None, True),
|
||
("来源", "text", False, None, ["planning", "detection", "extraction"]),
|
||
("状态", "enum", True, ["草稿", "已确认"], False),
|
||
]
|
||
|
||
|
||
def seed_one(conn, doc: dict, src_name: str):
|
||
"""单型入库:schema → version(v1) → fields → visibility_policy;返回审查摘要。"""
|
||
key = doc["target_type"]
|
||
status = "active" if doc.get("状态", "启用") == "启用" else "inactive"
|
||
# 1) schema 行 upsert(uk: tenant_id+domain+scope+target_type+schema_key)
|
||
row = conn.execute(
|
||
"""INSERT INTO muse_meta_schema (schema_key, domain, scope, target_type, display_name, status,
|
||
creator, updater, tenant_id)
|
||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
||
ON CONFLICT (tenant_id, domain, scope, target_type, schema_key)
|
||
DO UPDATE SET display_name=EXCLUDED.display_name, status=EXCLUDED.status, updater=EXCLUDED.updater
|
||
RETURNING id""",
|
||
(key, doc["domain"], doc["scope"], key, doc.get("中文名", key), status,
|
||
ACTOR, ACTOR, TENANT)).fetchone()
|
||
schema_id = row[0]
|
||
|
||
# 2) version v1 upsert(uk: tenant_id+schema_key+version_no);快照=完整 YAML
|
||
row = conn.execute(
|
||
"""INSERT INTO muse_meta_schema_version (schema_id, schema_key, version_no, status, active_flag,
|
||
field_contract_snapshot, change_note, published_by, activated_by, creator, updater, tenant_id)
|
||
VALUES (%s,%s,1,'published',TRUE,%s,%s,%s,%s,%s,%s,%s)
|
||
ON CONFLICT (tenant_id, schema_key, version_no)
|
||
DO UPDATE SET field_contract_snapshot=EXCLUDED.field_contract_snapshot,
|
||
change_note=EXCLUDED.change_note, updater=EXCLUDED.updater
|
||
RETURNING id""",
|
||
(schema_id, key, Jsonb(doc), f"A3 种子入库(源:{src_name})", ACTOR, ACTOR, ACTOR, ACTOR, TENANT)).fetchone()
|
||
version_id = row[0]
|
||
conn.execute("UPDATE muse_meta_schema SET active_version_id=%s WHERE id=%s", (version_id, schema_id))
|
||
|
||
# 3) field 行:删旧插新(幂等重建)
|
||
conn.execute("DELETE FROM muse_meta_field WHERE tenant_id=%s AND schema_version_id=%s", (TENANT, version_id))
|
||
ai_map = {}
|
||
n_special = 0
|
||
for i, (fk, ftype, req, enum, ai) in enumerate(BASE_FIELDS, start=1):
|
||
conn.execute(
|
||
"""INSERT INTO muse_meta_field (schema_version_id, field_key, display_name, field_type,
|
||
is_required, enum_values, sort_order, creator, updater, tenant_id)
|
||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)""",
|
||
(version_id, fk, fk, ftype, req, Jsonb(enum) if enum else None, i, ACTOR, ACTOR, TENANT))
|
||
ai_map[fk] = ai
|
||
for i, f in enumerate(doc.get("特有字段") or [], start=11):
|
||
conn.execute(
|
||
"""INSERT INTO muse_meta_field (schema_version_id, field_key, display_name, field_type,
|
||
is_required, sort_order, creator, updater, tenant_id)
|
||
VALUES (%s,%s,%s,'text',FALSE,%s,%s,%s,%s)""",
|
||
(version_id, f["key"], f["key"], i, ACTOR, ACTOR, TENANT))
|
||
ai_map[f["key"]] = f.get("aiContext", True)
|
||
n_special += 1
|
||
|
||
# 4) visibility_policy:版本级一行 upsert(无 uk,先查后写);字段级细则进 policy_snapshot
|
||
any_ai = any(v is not False for v in ai_map.values())
|
||
snap = Jsonb({"fieldAiContext": ai_map,
|
||
"用途枚举": ["planning", "generation", "detection", "extraction"],
|
||
"语义": "true=任何用途可入AI上下文;false=一律不入;[用途]=仅列出的用途可入"})
|
||
exist = conn.execute(
|
||
"SELECT id FROM muse_meta_visibility_policy WHERE tenant_id=%s AND schema_version_id=%s",
|
||
(TENANT, version_id)).fetchone()
|
||
if exist:
|
||
conn.execute("UPDATE muse_meta_visibility_policy SET ai_context=%s, policy_snapshot=%s, updater=%s WHERE id=%s",
|
||
(any_ai, snap, ACTOR, exist[0]))
|
||
else:
|
||
conn.execute(
|
||
"""INSERT INTO muse_meta_visibility_policy (schema_version_id, ui_visible, ai_context,
|
||
user_editable, user_searchable, exportable, policy_snapshot, creator, updater, tenant_id)
|
||
VALUES (%s,TRUE,%s,TRUE,FALSE,FALSE,%s,%s,%s,%s)""",
|
||
(version_id, any_ai, snap, ACTOR, ACTOR, TENANT))
|
||
|
||
full_open = sum(1 for v in ai_map.values() if v is True)
|
||
scoped = sum(1 for v in ai_map.values() if isinstance(v, list))
|
||
closed = sum(1 for v in ai_map.values() if v is False)
|
||
return (key, doc.get("中文名", ""), doc["domain"], doc["scope"], status,
|
||
len(BASE_FIELDS) + n_special, full_open, scoped, closed)
|
||
|
||
|
||
def main():
|
||
files = sorted(p for p in SCHEMA_DIR.glob("*.yaml"))
|
||
if not files:
|
||
sys.exit(f"未找到 schema YAML: {SCHEMA_DIR}")
|
||
rows = []
|
||
with psycopg.connect(DSN) as conn:
|
||
for p in files:
|
||
doc = yaml.safe_load(p.read_text())
|
||
rows.append(seed_one(conn, doc, p.name))
|
||
conn.commit()
|
||
# 审查面:逐型卡片行
|
||
print(f"{'型':<20}{'中文名':<10}{'domain':<11}{'scope':<10}{'状态':<9}{'字段':<5}{'AI全开':<7}{'限用途':<7}{'关闭'}")
|
||
for r in rows:
|
||
print(f"{r[0]:<20}{r[1]:<10}{r[2]:<11}{r[3]:<10}{r[4]:<9}{r[5]:<5}{r[6]:<7}{r[7]:<7}{r[8]}")
|
||
print(f"\n共 {len(rows)} 型入库(基础字段 {len(BASE_FIELDS)} + 各型特有;快照含判据/说明/设计发现全文)")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|