Skip to content

Commit e80e048

Browse files
committed
fix(kb): preserve complete replays without blocking baseline intake
1 parent 9414ee3 commit e80e048

7 files changed

Lines changed: 359 additions & 10 deletions

File tree

‎doc/architecture/SPEC/improve.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# improve/ —— 规范(元改进引擎)
22

3-
<!-- verified-against: 2026-10-08 -->
3+
<!-- verified-against: 2026-10-09 -->
44

55
`设计:/data/zhoutaichang/copilot/meta-improvement-engine-design.md v1(GPT-6 sol 批准 2026-09-29) · refactor-status: building (P0–P4 已落地)`
66

@@ -14,6 +14,8 @@
1414

1515
`objectives.py` 是受保护的父进程目标控制器(`objective/1`):自动导入可信 replay 输入、生成有因果见证的故障/负例、固定一个 API 模型、预注册三次配对及 item 聚类功效、核验实际源码与输入、独立计算分数及护栏。见证不传生成器或候选;候选数值分数无效。PR 与知识指标仅证明结构契约、产物保留与资源收益,`semantic_quality_claim=false`。引擎候选每次只修改归因或 lint;lint 既有正例及负例受保护。功效不足会存档并等待新鲜样本,不重放完成的付费调用。
1616

17+
Replay envelope 保留完整文件与引用,由 TraceStore 脱敏、压缩和按内容寻址;未压缩 UTF-8 JSON 上限为 64,000,000 bytes。超限只记录 `replay_capture_deferred` 的大小、上限和摘要并抛出专用 `ReplayCaptureLimit`,不保存截断输入,也不进入 replay 数据集。知识起草无活动进化工作流时继续原生固定生成器;活动工作流(含已禁用项)缺少完整 replay 时保持事件 pending。其他捕获错误不能走此延期路径。
18+
1719
`runtime.py` 重新应用补丁核验实际评估树,原子采用本地不可变 release,原生 PR 评审、知识草稿、lint 和归因在活动制品的沙箱中运行。宿主记录运行指纹才更新部署基线。首次八个不同 item 与上一版比较,七日缺证据自动恢复;保留后每周最多八个新 item 持续比较。契约、凭据/隔离/预算、指纹失败或资源显著退化自动恢复上一版;恢复制品不可验证则禁用执行。执行/切换锁及持久化激活、回滚意图支持重启恢复。
1820

1921
默认 `IMPROVE_EVALUATION_MODE=objective`、`IMPROVE_PROMOTION_MODE=automatic`,不要求人工标签、判官或 performance 模型。总开关仍关闭,固定模型取 `IMPROVE_EVOLVE_MODEL` 或 eco;每周一个候选、一次修复及共享 20 美元预算不变。`gold/pr` 模式保留原有双家族取证及人工流程。无隔离生产驱动的机械工作流仍可 lint,但不自动采用。当前主机命名空间关闭,代码候选实际执行延期。

‎doc/architecture/SPEC/kb_service.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,7 @@ dry run 为旧快照建本地临时 baseline,再生成相同升级串与全量
929929
## 自主进化接入
930930

931931
自主进化开启时起草前捕获事件与知识快照;已采用制品通过隔离代理起草 typed operations,宿主沿用已有知识发布权限与门禁。进化实验只在内存应用,不写产品知识库。
932+
完整 replay 的未压缩 UTF-8 JSON 上限为 64,000,000 bytes;超过上限记录 `replay_capture_deferred`,不截断全局规则 ID、tombstone 或外部引用。只有无活动 `kb-intake.draft` 制品时可继续原生固定生成器起草;活动项(包括已禁用项)或无法读取的注册表保持 pending,不执行未捕获的候选。其他捕获错误照常阻断。质量门、校准和发布授权不受此存储延期影响。
932933

933934
## 2026-10-05 通用功能发现:文档基线与源码扩展
934935

‎src/infermatrix_copilot/improve/objectives.py‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@
2222
ENGINE = 'workflow-improve.improve.forensics'
2323
DRIVERS = {'pr-review': 'pr-review', 'kb-intake': 'kb-intake', 'meta': 'objective-meta', 'mechanical': 'objective-tests'}
2424
FAULTS = {'ceiling': ('S4', 'L01'), 'discard': ('S3', 'L09'), 'fallback': ('S7', 'L07')}
25+
# Complete KB trees and companion texts already exceed 16 MB. TraceStore
26+
# compresses these envelopes; retain a finite uncompressed memory/disk bound.
27+
REPLAY_INPUT_MAX_BYTES = 64_000_000
28+
29+
30+
class ReplayCaptureLimit(artifacts.ArtifactError):
31+
"""A complete replay exceeds the storage bound, before candidate execution."""
32+
def __init__(self, size_bytes, limit_bytes):
33+
self.size_bytes, self.limit_bytes = size_bytes, limit_bytes
34+
super().__init__(f'replay input exceeds capture limit ({size_bytes} > {limit_bytes} bytes)')
2535

2636

2737
def enabled(settings):
@@ -81,9 +91,17 @@ def capture(store, workflow, item, payload):
8191
forbidden = {'labels', 'gold', 'scores', 'judgments', 'report'}
8292
if forbidden & set(payload): raise artifacts.ArtifactError('replay input includes evaluator material')
8393
raw = json.dumps(payload, ensure_ascii=False)
84-
if len(raw.encode()) > 16_000_000: raise artifacts.ArtifactError('replay input exceeds capture limit')
85-
store.append('decision', context={'workflow': workflow, 'item': item},
86-
inputs={'evolution_input': raw}, result={'type': 'replay_input', 'input_sha': artifacts.digest(raw.encode())})
94+
encoded = raw.encode()
95+
context = {'workflow': workflow, 'item': item}
96+
input_sha = artifacts.digest(encoded)
97+
if len(encoded) > REPLAY_INPUT_MAX_BYTES:
98+
# A missing replay is explicit and never eligible for collection. Do
99+
# not truncate files or references that affect the executable contract.
100+
store.append('decision', context=context, result={'type': 'replay_capture_deferred', 'reason': 'size_limit',
101+
'input_bytes': len(encoded), 'limit_bytes': REPLAY_INPUT_MAX_BYTES, 'input_sha': input_sha})
102+
raise ReplayCaptureLimit(len(encoded), REPLAY_INPUT_MAX_BYTES)
103+
store.append('decision', context=context,
104+
inputs={'evolution_input': raw}, result={'type': 'replay_input', 'input_sha': input_sha})
87105

88106

89107
def collect(settings, store, workflow):

‎src/infermatrix_copilot/kb_service/runtime.py‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,16 +256,31 @@ def _run_intake_locked(rt: KbRuntime, lifecycle: RepoLifecycle, *, max_events: i
256256
from ..improve import objectives
257257
prediction = None
258258
if objectives.enabled(draft_settings) and draft_settings.improve_enabled and draft_settings.improve_evolve_enabled and rt.traces is not None:
259-
from ..improve.runtime import execute
259+
from ..improve.runtime import active, execute
260260
payload = {"repo": lifecycle.repo, "repo_dir": lifecycle.knowledge_dir, "event_id": event["id"],
261261
"evidence": event["payload"], "files": base, "release": release, "today": today,
262262
"external_texts": external, "generator_model": rt.generator.model}
263-
objectives.capture(rt.traces, "kb-intake.draft", f"{lifecycle.repo}#{event['id']}", payload)
264-
from ..llm import LLM
265263
try:
266-
prediction = execute(draft_settings, rt.traces, "kb-intake.draft", payload, LLM(draft_settings))
267-
except Exception as exc:
268-
raise ModelUnavailable("autonomous draft rolled back: " + str(exc)) from exc
264+
objectives.capture(rt.traces, "kb-intake.draft", f"{lifecycle.repo}#{event['id']}", payload)
265+
except objectives.ReplayCaptureLimit as exc:
266+
# Ordinary pinned drafting does not require an evolution
267+
# replay. An active (even disabled) evolved workflow does.
268+
try:
269+
entry = active(draft_settings)
270+
workflows = entry.get("workflows", [])
271+
if not isinstance(workflows, list) or not all(isinstance(w, str) for w in workflows):
272+
raise ValueError("invalid active draft workflows")
273+
is_active = "kb-intake.draft" in workflows
274+
except Exception as registry_exc:
275+
raise ModelUnavailable("cannot verify active draft release: " + str(registry_exc)) from registry_exc
276+
if is_active:
277+
raise ModelUnavailable("active draft lacks a complete replay: " + str(exc)) from exc
278+
else:
279+
from ..llm import LLM
280+
try:
281+
prediction = execute(draft_settings, rt.traces, "kb-intake.draft", payload, LLM(draft_settings))
282+
except Exception as exc:
283+
raise ModelUnavailable("autonomous draft rolled back: " + str(exc)) from exc
269284
if prediction is not None:
270285
from .intake import Draft
271286
from ..knowledge_service.ops import KnowledgeOperation

‎test/test_autonomous_evolution.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,42 @@ def test_trace_inputs_are_automatically_frozen_and_clustered(tmp_path):
198198
with pytest.raises(artifacts.ArtifactError): objectives.rows(st, 'kb-intake.draft')
199199

200200

201+
def test_full_kb_replay_larger_than_old_limit_round_trips(tmp_path):
202+
from infermatrix_copilot.trace_store import TraceStore
203+
st = Settings(_env_file=None, improve_ledger_dir=str(tmp_path / 'ledger'))
204+
store = TraceStore(tmp_path / 'trace')
205+
payload = {'repo': 'demo', 'files': {'repos/demo/a.md': 'knowledge'},
206+
'external_texts': {'adapters/demo/reference.md': '文' * 5_400_000}}
207+
assert len(json.dumps(payload, ensure_ascii=False).encode()) > 16_000_000
208+
objectives.capture(store, 'kb-intake.draft', 'demo#1', payload)
209+
assert objectives.collect(st, store, 'kb-intake.draft') == 1
210+
row = objectives.rows(st, 'kb-intake.draft')[0]
211+
assert row['payload'] == payload
212+
213+
214+
def test_oversized_replay_is_explicit_and_ineligible(tmp_path, monkeypatch):
215+
from infermatrix_copilot.trace_store import TraceStore
216+
st = Settings(_env_file=None, improve_ledger_dir=str(tmp_path / 'ledger'))
217+
store = TraceStore(tmp_path / 'trace')
218+
monkeypatch.setattr(objectives, 'REPLAY_INPUT_MAX_BYTES', 80)
219+
payload = {'files': {'other.md': '文' * 30}}
220+
size = len(json.dumps(payload, ensure_ascii=False).encode())
221+
assert len(json.dumps(payload, ensure_ascii=False)) < 80 < size
222+
with pytest.raises(objectives.ReplayCaptureLimit) as exc:
223+
objectives.capture(store, 'kb-intake.draft', 'demo#1', payload)
224+
assert (exc.value.size_bytes, exc.value.limit_bytes) == (size, 80)
225+
record, = store.query(workflow='kb-intake.draft')
226+
assert record['inputs'] == {}
227+
assert record['result'] == {'type': 'replay_capture_deferred', 'reason': 'size_limit',
228+
'input_bytes': size, 'limit_bytes': 80,
229+
'input_sha': artifacts.digest(json.dumps(payload, ensure_ascii=False).encode())}
230+
assert objectives.collect(st, store, 'kb-intake.draft') == 0
231+
assert objectives.rows(st, 'kb-intake.draft') == []
232+
with pytest.raises(artifacts.ArtifactError, match='evaluator material') as exc:
233+
objectives.capture(store, 'kb-intake.draft', 'demo#2', {'labels': []})
234+
assert not isinstance(exc.value, objectives.ReplayCaptureLimit)
235+
236+
201237
def test_empty_kb_is_not_rewarded_as_an_error():
202238
row = {'payload': {'files': {}, 'repo_dir': 'repos/demo', 'release': 'v1', 'today': '2026-10-04'}}
203239
assert objectives.score('kb-intake', row, {'operations': [], 'rejected': False}) == 1

0 commit comments

Comments
 (0)