diff --git a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md index 64cbff9006..4101766d1f 100644 --- a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md +++ b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md @@ -673,6 +673,43 @@ not authorize a generic schema framework. - Wheel and sdist are installed into fresh environments and execute deep semantic probes from packaged files. +#### Caller-observable semantic parity is a promotion gate + +Every Python-to-TypeScript cutover inventories the behavior of every production +caller branch before implementation. The inventory covers accepted input and +default normalization; supplied, omitted, empty, and explicit-clear arguments; +eligibility and overlapping-rejection precedence; complete diagnostics and +remediation; dispatch-to-persistence readback; authority, ownership, receipt, +and no-effect outcomes; and replay or concurrent updates when the transaction +supports them. Equal reason codes or successful provider conformance do not +establish parity. + +The cutover PR records machine-replayable execution receipts for an immutable +baseline revision and the exact reviewed head. Both runs use the same bounded +script, synthetic fixture fingerprint, public production entrypoint, and real +affected backend unless an intentional delta is declared and independently +approved. Each receipt names the revision, command, backend, exit status, +normalized observation fingerprint, and public-safe evidence pointer or inline +observation. Normalization may remove documented nondeterminism such as a +temporary path or timestamp, but never diagnostics, field presence, precedence, +persisted state, identity, ownership, or effects. + +The same harness must demonstrate regression sensitivity: it fails an +independently stated invariant on the historical defect or a deliberate +semantic mutation, such as dropping a field or diagnostic detail or adding a +stronger precondition, and passes on the fixed head. A unit test that bypasses +the production entrypoint, or a suite in which every provider already shares +the candidate rule, is supporting coverage rather than baseline/head proof. If +the real backend or immutable baseline cannot be exercised safely, promotion is +held as `not_yet_proven`; prose cannot waive the gap. + +This qualification is offline evidence, not a second authority. Production +does not dual-run Python and TypeScript, derive expected results from the +candidate, or retain the legacy rule after cutover. Intentional behavior changes +are separated from parity rows, justified against the public contract, and +approved explicitly. After promotion, only fixtures that express durable public +or persisted semantics remain. + Characterization output is evidence, not specification. If a pinned behavior contradicts an independently reviewed invariant, the PR must disclose and separately approve the behavior change. Once the old authority is removed, diff --git a/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md b/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md index bcbe1fa7e5..be60746292 100644 --- a/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md +++ b/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md @@ -566,6 +566,32 @@ happy path 及其 retry/recovery path 上实测,不能由 handler 数量推断 - 进程 crash 与 retry 不得重复已经提交的内部 effect。 - wheel 与 sdist 安装到全新环境后,从打包文件执行 deep semantic probe。 +#### Caller 可观测语义是 promotion 门禁 + +每笔 Python 到 TypeScript cutover 在实现前都要盘点所有生产 caller 分支的行为。盘点 +包括:可接受输入与默认归一化;已传入、未传入、空值与显式清除参数;资格与重叠拒绝 +的优先级;完整诊断与修复建议;从 dispatch 到持久化后独立 readback;authority、 +ownership、receipt 与 no-effect 结果;以及 transaction 支持时的 replay 或并发更新。 +只有相同 reason code,或 provider conformance 通过,不足以证明 parity。 + +Cutover PR 必须分别为不可变基线 revision 和精确审查 head 记录机器可重放的执行 +receipt。除非声明且独立批准有意差异,两次运行必须使用同一有界脚本、合成 fixture +指纹、公开生产入口与真实受影响 backend。每个 receipt 都要写明 revision、命令、 +backend、退出状态、归一化观测指纹,以及公开安全的证据指针或内联观测。归一化可以 +消除临时路径、时间戳等已记录的非确定性,但不得消除诊断、字段存在性、优先级、 +持久状态、identity、ownership 或 effect 差异。 + +同一 harness 还必须证明回归敏感性:它要在历史缺陷或一个故意注入的语义 mutation +上使独立定义的 invariant 失败,并在修复 head 上通过。Mutation 例如丢弃字段或诊断 +细节,或引入更强前置条件。绕过生产入口的单测,或所有 provider 都已共享候选规则 +的测试集,只能算辅助覆盖,不是 baseline/head 证明。如果无法安全运行真实 backend +或不可变基线,promotion 必须以 `not_yet_proven` 暂停;文字说明不能豁免该缺口。 + +这项验证是离线证据,不是第二份 authority。生产环境不同时运行 Python 与 TypeScript, +不从候选实现推导期望结果,cutover 后不保留 legacy rule。有意行为变更必须与 parity row +分开,根据公开 contract 说明理由并显式批准。Promotion 后只保留表达持久公开或 +持久化语义的 fixture。 + Characterization output 是证据,不是 specification。Pinned 行为若与独立 review 的 invariant 冲突,PR 必须披露,并把行为变更单独批准。旧 authority 删除后,promotion 还要求删除只服务这次实现对比的 characterization machinery;当 fixture 表达 public diff --git a/docs/development/testing-and-quality.md b/docs/development/testing-and-quality.md index e3c6838696..212f54b101 100644 --- a/docs/development/testing-and-quality.md +++ b/docs/development/testing-and-quality.md @@ -104,6 +104,21 @@ runtime regressions must still test actual behavior. 不能盲目追求字节一致。复审重查完整兼容清单,不沿着上一条发现自动走向批准。缺少 对照证据就不能宣称等价;packet 测试只证明要求被投影,不证明 agent 已执行或产品无缺陷。 +The evidence must be executable, not merely descriptive. Record a replayable +command or bounded script invocation for baseline, exact head, and the +sensitivity case, together with the real affected backend, immutable fixture +fingerprint, exit status, and normalized observation fingerprint. Baseline and +head use the same harness and fixture unless an intentional delta is declared. +The mutation run must traverse the same public entrypoint and make an +independent oracle fail; helper-only unit coverage or conformance among +providers that all share the candidate rule cannot satisfy this gate. + +证据必须可执行,不只是文字描述。基线、精确 head 和敏感性用例都要记录可重放命令 +或有界脚本调用,以及真实受影响 backend、不可变 fixture 指纹、退出状态和归一化观测 +指纹。除非声明并证明有意差异,baseline 与 head 必须使用同一 harness 和 fixture。 +Mutation 运行必须经过同一 public entrypoint,并让独立 oracle 失败;只测 helper,或让共享 +候选规则的多个 provider 互相 conformance,不能通过该门禁。 + Refactors must exercise the affected production entrypoint and real backend before delivery. Unit tests, mocks, and in-memory conformance remain useful, but cannot replace that proof. For changes affecting PostgreSQL authority, diff --git a/loopx/capabilities/pr_review_queue/README.md b/loopx/capabilities/pr_review_queue/README.md index 5a71ae626e..28bdd21d7b 100644 --- a/loopx/capabilities/pr_review_queue/README.md +++ b/loopx/capabilities/pr_review_queue/README.md @@ -186,6 +186,17 @@ typed evidence groups before a verdict: with evidence, not green CI or conflict-free coexistence. For alternative views of one resource, validate consumer switching and concurrent updates where relevant. Repeat the comparison after base integration or head changes; +- caller-observable semantic parity for every behavior-bearing change, whether + or not its title says refactor or migration. Inventory legacy caller branches, + then run the same synthetic fixture through the public entrypoint and affected + backend at an immutable baseline and the exact head. Record replayable + commands, revisions, fixture and observation fingerprints, exit status, full + diagnostics, persisted readback, authority/effect outcomes, and only the + normalization rules needed for documented nondeterminism. The sensitivity + case must make the real path fail on the historical defect or a deliberate + dropped-field/detail or stronger-precondition mutation, then pass at the fixed + head. New-rule provider conformance and prose-only claims do not establish + before/after compatibility; - exact changed-line classification across production, tests/fixtures, docs, generated output, and mechanical moves; - a 2-5 item exact-head symbol map for code-changing PRs, including caller, diff --git a/loopx/capabilities/pr_review_queue/review_contract.py b/loopx/capabilities/pr_review_queue/review_contract.py index 307164a860..1a0c5881f7 100644 --- a/loopx/capabilities/pr_review_queue/review_contract.py +++ b/loopx/capabilities/pr_review_queue/review_contract.py @@ -193,6 +193,8 @@ def build_review_execution_contract() -> dict[str, Any]: "reviewed_head", "caller_branch_inventory", "comparison_rows", + "execution_receipts", + "normalization_rules", "intentional_deltas", "regression_sensitivity", "unverified_dimensions", @@ -214,6 +216,24 @@ def build_review_execution_contract() -> dict[str, Any]: "expected_invariant_source", "validation_evidence", ], + "execution_receipt_fields": [ + "revision", + "command", + "public_entrypoint", + "backend", + "fixture_fingerprint", + "exit_status", + "observation_fingerprint", + "public_safe_artifact_reference_or_inline_observation", + ], + "regression_sensitivity_fields": [ + "invariant", + "historical_defect_or_deliberate_mutation", + "command", + "expected_failure", + "observed_failure", + "passing_head_receipt", + ], "rule": ( "Inventory changed and bypassed caller branches from the immutable " "pre-change baseline, not just the new helper or PR title. Compare " @@ -234,7 +254,16 @@ def build_review_execution_contract() -> dict[str, Any]: "baseline bugs require explicitly disclosed, justified changes rather " "than blind byte parity. Show a regression failing on the old defect " "or a deliberate semantic mutation (dropped argument/detail, stronger " - "precondition), then passing on the fix. Normalize only documented " + "precondition), then passing on the fix. Each baseline/head execution " + "receipt must record a replayable command or bounded script invocation, " + "the public entrypoint, real affected backend, immutable fixture " + "fingerprint, exit status, and normalized observation fingerprint. Use " + "the same harness and fixture at both revisions unless an intentional " + "delta explains the difference. Mutation evidence must execute that " + "public path and make an independent oracle fail before the fixed-head " + "receipt passes; prose, helper-only unit coverage, parser acceptance, or " + "provider conformance against only the new rule is insufficient. " + "Normalize only documented " "nondeterminism, never away a semantic delta. Re-review the full " "inventory after fixes, not only the last finding. Missing baseline " "or real-path evidence is not_yet_proven, not equivalent. This is a " diff --git a/tests/capabilities/test_pr_review_contract.py b/tests/capabilities/test_pr_review_contract.py index 44f12182c8..f0c3ef94eb 100644 --- a/tests/capabilities/test_pr_review_contract.py +++ b/tests/capabilities/test_pr_review_contract.py @@ -389,6 +389,8 @@ def test_observable_semantics_covers_diagnostics_and_claim_neutral_note_paths() "reviewed_head", "caller_branch_inventory", "comparison_rows", + "execution_receipts", + "normalization_rules", "intentional_deltas", "regression_sensitivity", "unverified_dimensions", @@ -410,6 +412,24 @@ def test_observable_semantics_covers_diagnostics_and_claim_neutral_note_paths() "expected_invariant_source", "validation_evidence", } <= set(parity["row_fields"]) + assert { + "revision", + "command", + "public_entrypoint", + "backend", + "fixture_fingerprint", + "exit_status", + "observation_fingerprint", + "public_safe_artifact_reference_or_inline_observation", + } <= set(parity["execution_receipt_fields"]) + assert { + "invariant", + "historical_defect_or_deliberate_mutation", + "command", + "expected_failure", + "observed_failure", + "passing_head_receipt", + } <= set(parity["regression_sensitivity_fields"]) assert parity["verdict_values"] == [ "equivalent", "intentional_change_validated", @@ -417,6 +437,9 @@ def test_observable_semantics_covers_diagnostics_and_claim_neutral_note_paths() "not_yet_proven", ] assert "reviewer-executed" in parity["rule"] + assert "replayable command" in parity["rule"] + assert "real affected backend" in parity["rule"] + assert "independent oracle fail" in parity["rule"] assert ( "observable_semantics" in contract["verdict_policy"]["open_pr_unresolved_semantics"]