Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9e6dc84
docs(semantics): define relative completeness boundary
songoow Sep 15, 2026
9b71c5c
docs(semantics): align formal value domains
songoow Sep 15, 2026
34e43f1
feat(pr-review): surface semantic alignment constraints
songoow Sep 15, 2026
92f9e36
chore(semantics): refresh inventory for review contract
songoow Sep 15, 2026
c35c35f
Merge remote-tracking branch 'upstream/main' into codex/pr-review-sem…
songoow Sep 15, 2026
6902499
fix(pr-review): scope semantic evidence to affected contracts
songoow Sep 15, 2026
940f518
docs(pr-review): define semantic triage and repair boundaries
songoow Sep 15, 2026
673afad
Merge remote-tracking branch 'upstream/main' into codex/pr-review-sem…
songoow Sep 15, 2026
732acf0
docs(development): use uv for current-checkout validation
songoow Sep 15, 2026
c19aa76
Merge main and refresh semantic review inventory
songoow Sep 16, 2026
1c385c4
Merge current main into semantic proof-boundary documentation
songoow Sep 16, 2026
e32659f
fix(semantics): unify proof domains and candidate guidance
songoow Sep 16, 2026
bcc19d4
Merge current main before semantic dependency coordination
songoow Sep 16, 2026
01db805
Integrate the semantic proof-boundary prerequisite for PR review
songoow Sep 16, 2026
3164b85
Refresh semantic foundation for the current Turn lane baseline
songoow Sep 16, 2026
d12be19
test(chat): isolate unbound channel fixture from installed runtime
songoow Sep 16, 2026
f1c3a83
Refresh review prerequisite chain with current main and isolated chan…
songoow Sep 16, 2026
2db1273
Merge latest main and drop duplicate channel fixture hunk
songoow Sep 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,20 @@ Before adding or consolidating a public smoke, use the bilingual
[good smoke guide](docs/development/good-smokes.md) to define its durable
invariant, independent oracle, cadence, and public-safe fixture boundary.

Install and verify the checkout:
For source development, run commands from the repository or dedicated worktree
root with `uv`. It manages a compatible Python and installs the current checkout
in the project environment, keeping checks separate from a globally installed
LoopX release. See the [local validation commands](docs/development/testing-and-quality.md#local-validation-environment--本地验证环境)
for environment, lockfile, and CI boundaries.

```bash
git clone https://github.com/huangruiteng/loopx ~/loopx
~/loopx/scripts/install-local.sh
export PATH="$HOME/.local/bin:$PATH"
loopx doctor
loopx demo
```

Common focused checks:

```bash
python -m pip install -e ".[test]"
python -m ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentation
python -m mypy
python examples/control_plane/cli-output-budget-regression-smoke.py
python -m pytest -q
loopx canary premerge --from-git-diff
loopx check --scan-path loopx/ --scan-path tests/ --scan-path examples/ --scan-path docs/
uv sync --extra test
uv run --extra test python -m ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentation
uv run --extra test python -m mypy
uv run --extra test python examples/control_plane/cli-output-budget-regression-smoke.py
uv run --extra test python -m pytest -q
uv run --extra test loopx canary premerge --from-git-diff
uv run --extra test loopx check --scan-path loopx/ --scan-path tests/ --scan-path examples/ --scan-path docs/
git diff --check
```

Expand Down
135 changes: 107 additions & 28 deletions docs/architecture/rfcs/semantic-vocabulary-convergence-v0.md

Large diffs are not rendered by default.

114 changes: 89 additions & 25 deletions docs/architecture/rfcs/semantic-vocabulary-convergence-v0.zh-CN.md

Large diffs are not rendered by default.

65 changes: 56 additions & 9 deletions docs/development/testing-and-quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,22 +331,69 @@ negative 或 mutation-style 断言,并让各 provider 复用同一 envelope
经 review 的兼容理由,不得削弱或删除已有维度。禁止复制生产文本、标识、路径、日志、
凭据或私有快照。PR 验证证据需报告 fixture schema、语义维度、provider arms 与有意差异。

Install the test dependencies once:
### Local Validation Environment / 本地验证环境

Run from the repository or dedicated worktree root with `uv`. The project's
`requires-python` declares Python `>=3.11`; it does not require an executable
named `python3.11`. `uv` selects a compatible interpreter, creates `.venv`, and
installs the checkout with the selected extras. Interpreter downloads depend on
uv's download settings and network access. A system `python3` may be too old,
and a global `loopx` may resolve to a different installed source tree.

在仓库或独立 worktree 根目录使用 `uv`。`pyproject.toml` 要求 Python `>=3.11`,
无需依赖名为 `python3.11` 的命令。uv 选择兼容解释器,在 `.venv` 中安装当前源码
与测试依赖;能否自动下载 Python 取决于下载配置与网络。系统 `python3` 可能过旧,
全局 `loopx` 也可能指向另一个已安装版本。

```bash
python -m pip install -e ".[test]"
uv sync --extra test
uv run --extra test python -m ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentation
uv run --extra test python -m mypy
uv run --extra test python examples/control_plane/cli-output-budget-regression-smoke.py
uv run --extra test python -m pytest -q
uv run --extra test loopx canary premerge --from-git-diff
# For a fork whose PR base is upstream/main, use this instead:
uv run --extra test loopx canary premerge --from-git-diff --git-diff-base upstream/main
# Run one semantic smoke or check its generated inventory:
uv run --extra test loopx canary smoke-suite --script semantic-vocabulary-drift-smoke.py
uv run python scripts/generate_semantic_inventory.py --check
git diff --check
```

Run the fast repository gate:
Confirm the interpreter and imported checkout when diagnosing a mismatch:

```bash
python -m ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentation
python -m mypy
python examples/control_plane/cli-output-budget-regression-smoke.py
python -m pytest -q
git diff --check
uv run python -c "import sys, loopx; print(sys.executable); print(loopx.__file__)"
```

Canary executes Python checks with the interpreter that launched LoopX
(`sys.executable`). Its displayed `python3` command is not a second interpreter
selection. Keep subprocesses on `sys.executable`; use `uv run` at the developer
entrypoint. Avoid `uvx loopx` or `uv run --no-project` when validating this
checkout, and change into the intended worktree before running Git-based checks.
An activated compatible environment remains a supported alternative: install
with `python -m pip install -e ".[test]"`, then use that environment's Python
and LoopX commands directly.

Canary 使用启动 LoopX 的 `sys.executable` 执行 Python 检查,显示的 `python3`
不是重新选择解释器。子进程继续复用 `sys.executable`,只在开发入口使用 `uv run`。
检查当前源码时不要改用 `uvx loopx` 或 `uv run --no-project`;Git diff 检查前先进入
目标 worktree。已有兼容虚拟环境也可用 `python -m pip install -e ".[test]"` 安装源码,
随后直接使用该环境的命令。

The repository does not currently track `.python-version` or `uv.lock`. `uv`
creates a local lockfile during resolution; keep that generated file out of
unrelated PRs. Introducing a shared lock or interpreter pin is a separate
repository policy change. Do not claim identical environments from
`requires-python` alone, or use `--locked` before a reviewed lockfile exists.
CI keeps its explicit Python versions and pinned/hash-checked installation
paths. Historical validation receipts keep the commands that actually ran.

当前仓库未跟踪 `.python-version` 或 `uv.lock`。uv 解析依赖时生成的本地锁文件不要
混入无关 PR;共享锁文件与解释器版本固定应单独评审。最低版本要求不等于环境完全
可复现,没有已评审锁文件时也不使用 `--locked`。CI 保留显式 Python 版本与固定依赖/
哈希校验的安装路径,历史验证记录保留实际执行过的命令。

`.github/workflows/python-tests.yml` runs this fast lane for relevant Python
pull requests. It intentionally excludes provider-backed evaluation and the
full smoke catalog, so ordinary iteration does not depend on credentials,
Expand Down Expand Up @@ -377,7 +424,7 @@ Sonar 只复用同一次 run 的 XML,不重复测试、不跨 run 取产物。
Sonar,测试 job 不接收 Sonar secret。触发范围取原有两套 workflow 的并集;纯前端
PR 使用前述豁免,Sonar 配置变更仍全量运行,包括没有 token 的 fork。

Reproduce one shard locally with `python -m pytest -q -n 2 --splits 4 --group 1
Reproduce one shard locally with `uv run --extra test python -m pytest -q -n 2 --splits 4 --group 1
--splitting-algorithm least_duration --cov=loopx`. Omit the split arguments to
run the complete suite locally. 全量本地测试仍省略分片参数即可。

Expand Down
2 changes: 2 additions & 0 deletions examples/pr-review-command-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,7 @@ def fake_run_gh_json(args: list[str], *, cwd: Path | None = None) -> object:
"behavior_change_disclosure",
"guidance_vs_obligation",
"durable_smoke_value",
"semantic_alignment",
}, requirements
assert requirements["symbol_map"]["item_count"] == {"minimum": 2, "maximum": 5}
assert "caller_evidence" in requirements["symbol_map"]["item_fields"]
Expand Down Expand Up @@ -966,6 +967,7 @@ def fake_run_gh_json(args: list[str], *, cwd: Path | None = None) -> object:
"change_proportionality": ["disproportionate", "not_yet_proven"],
"default_off_isolation": ["not_isolated", "not_yet_proven"],
"authority_semantics": ["misleading", "not_yet_proven"],
"semantic_alignment": ["not_yet_proven", "violated"],
}
assert execution["finding_contract"]["findings_first"] is True
first_plan = first["review_plan"]
Expand Down
25 changes: 21 additions & 4 deletions examples/semantic-vocabulary-drift-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
STATUSES = {"canonical", "legacy", "merge_candidate"}
FORMAL_MODEL_KEYS = {
"schema_version", "universes", "roles", "role_hierarchy", "relations", "invariants", "proof_boundary",
"enforcement_policy",
"enforcement_policy", "candidate_decisions",
}
FORMAL_MODEL_SCHEMA_VERSION = "loopx_semantic_formal_model_v0"
FORMAL_UNIVERSE_KEYS = {"vocabularies", "values", "sites", "scopes", "roles"}
Expand All @@ -67,6 +67,15 @@
}
FORMAL_ENFORCEMENT = {"m0", "m0_5", "m1", "advisory", "unproved"}
FORMAL_POLICY_KEYS = {"blocking_now", "blocking_next", "advisory", "unproved"}
FORMAL_CANDIDATE_DECISIONS = {
"reuse_existing",
"extend_vocabulary",
"create_vocabulary",
"local_only",
"external_input",
"compatibility_only",
"unknown",
}

# Hard ceiling on the registry's own floors and budgets, kept in code rather than
# in the registry so one single-diff edit to ``vocabulary_v0.json`` cannot relax
Expand Down Expand Up @@ -230,9 +239,17 @@ def check_formal_model(model: dict[str, Any]) -> None:
for policy_name, ids in policy.items():
require(all(stages[item_id] == stage_for_policy[policy_name] for item_id in ids),
f"formal_model policy lane {policy_name} disagrees with invariant enforcement stage")
candidates = model["candidate_decisions"]
require(set(candidates) == {"values", "default", "meaning"},
"formal_model candidate_decisions must define values, default, and meaning")
require(candidates["values"] == sorted(FORMAL_CANDIDATE_DECISIONS),
"formal_model candidate_decisions must be a stable exhaustive classification")
require(candidates["default"] == "unknown",
"formal_model candidate_decisions must default unresolved candidates to unknown")
require(candidates["meaning"].strip(), "formal_model candidate_decisions needs a meaning")
boundary = model["proof_boundary"]
require(set(boundary) == {"established", "bounded", "unproved"},
"formal_model proof_boundary must separate established, bounded, and unproved claims")
require(set(boundary) == {"established", "bounded", "unknown", "unproved"},
"formal_model proof_boundary must separate established, bounded, unknown, and unproved claims")
for key in boundary:
require(isinstance(boundary[key], list) and all(isinstance(value, str) and value.strip() for value in boundary[key]),
f"formal_model proof_boundary.{key} must contain non-empty claim names")
Expand Down Expand Up @@ -450,7 +467,7 @@ def check_inventory(registry: dict[str, Any], sources: list[SourceFile]) -> tupl
committed = inventory_path.read_text(encoding="utf-8")
inventory = build_inventory(REPO_ROOT, sources=sources)
require(inventory["schema_version"] == INVENTORY_SCHEMA_VERSION, "inventory schema drift")
require(render_inventory(inventory) == committed, f"{registry['inventory']} is stale; run python3.11 scripts/generate_semantic_inventory.py and commit the result")
require(render_inventory(inventory) == committed, f"{registry['inventory']} is stale; from the repository root run uv run python scripts/generate_semantic_inventory.py and commit the result")
ratchets = registry["inventory_ratchets"]
summary = inventory["summary"]
parts = []
Expand Down
72 changes: 72 additions & 0 deletions loopx/capabilities/pr_review_queue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,78 @@ progress toward approval by themselves; the reviewer should request the
smallest viable fix, deletion, split, or hold when the benefit does not justify
the accumulated mechanism.

### Semantic alignment and CI constraint recovery

Policy revision 5 replaces the universal detailed semantic review with bounded
triage for code and behavior-bearing policy changes. The required row starts
with `checked_scope`, `impact_reason`, and `verdict` (plus the standard evidence
`status`). Review the full diff and relevant definitions/callers, then stop at
`not_applicable` if no shared contract is affected. No candidate value, separate
report subsection, full RFC read, or hypothetical repair is needed on this path.
A changed-file preview or an unchanged registry alone cannot establish no impact.

| Verdict | Additional evidence | Approval effect |
| --- | --- | --- |
| `not_applicable` | None; scope and reason explain the absence of shared contract impact | No semantic blocker |
| `aligned` / `new_semantics_justified` | `candidate_decision`, `affected_contract`, `evidence_refs` to existing review evidence | No semantic blocker |
| `advisory` | `candidate_decision`, `analysis_limit`; the impact reason explains why no affected current obligation lacks required evidence | Reports a bounded-analysis gap, without claiming safety or blocking on that gap alone |
| `not_yet_proven` / `violated` | `candidate_decision`, `affected_contract`, `trigger`, `observed_evidence`, `minimum_repair`, `validation_commands` | Blocks approval for an affected current obligation with missing required evidence or a concrete violation |

For shared states, owners, consumer domains, projections or persistence changes,
read only the affected base/head contracts. Reuse `repository_reuse`,
`observable_semantics`, and `validation_matrix` evidence instead of repeating
it. Docs-only reviews may supply the same row when they find contract impact;
a supplied row is checked even when not required by the plan. A generated
inventory change alone does not require a detailed semantic review.

The generic capability does not infer impact from repository-specific paths or
inject a repository's check names into another repository's packet. Resolve
obligations from the target repository's current policy and use the existing
validation matrix. Observed check runs do not establish which checks are required.
Green CI proves only the checks that ran, not whole-program convergence.

The repair map below is a **LoopX repository example**, not a policy for every
`--repo`. In LoopX, verify required checks against `.github/GOVERNANCE.md` and the
current CI configuration: `Sign-off` and `merge-gate` are the documented checks;
the semantic smoke runs through Python tests. Full Public Smokes is a
post-merge/scheduled surface. These facts are not copied into generic packets.

Use this repair map when a check fails:

| Failure family | What it means | Minimum repair | Do not repair by |
| --- | --- | --- | --- |
| `Sign-off` | One commit in the PR range lacks a valid DCO trailer | Add `Signed-off-by` to every affected commit with `git commit --amend -s` or an equivalent history repair; verify the full range | Signing only the newest commit |
| semantic smoke: unregistered value | A recognised carrier/field form introduced a value outside the registry | Reuse the existing owner value, or add the value with its owner, slot, scope, tests, and RFC evidence | Registering an unrelated string to silence the error |
| semantic smoke: stale inventory | The committed generated map no longer matches the indexed source tree | Stage intended source paths, run `uv run python scripts/generate_semantic_inventory.py`, then `--check` | Editing counts by hand or including private/untracked files |
| semantic smoke: owner/parity | A defining symbol or Python/TypeScript value set diverged | Restore the single owner or deliberately update both runtime owners with parity evidence | Adding a second silent authority |
| semantic smoke: projection | A source value is unmapped, mapped to the wrong target, or should be rejected explicitly | Update the declared mapping and executable owner together, then test the boundary case | Deleting a source value without compatibility analysis |
| semantic smoke: budget/anchor | Measured debt grew or the guard was weakened | Fix the underlying duplicate/coverage issue and lower a budget only when the measured debt really fell | Raising the budget, narrowing the scan root, or renaming to hide drift |
| `merge-gate` | A required upstream CI job failed, was skipped unexpectedly, or has incomplete qualification | Inspect `needs` and the failing job, fix the owning path, and rerun at the same head | Treating a local smoke as proof that the remote gate is complete |

A blocker must connect the exact-head change to an existing obligation and a
replayable failure or missing required validation. An untraceable dynamic value
alone is an `advisory`, not `not_yet_proven` and not proof of safety. Removing a
persisted value without required old-state readback is `not_yet_proven`; a failed
required projection check is `violated`. Neither can be downgraded to advisory
to bypass CI or a concrete blocking finding. Do not turn future or advisory RFC
properties into current merge obligations. Result checking validates declared
consistency, not the truth of a reviewer's classification.

**中文边界:** 普通改动只填检查范围、影响理由与结论,无共享契约影响时用
`not_applicable` 结束,无需虚构候选值或补齐整张证据表。影响共享状态、owner、
消费者、投影或持久化时,按受影响契约检查并引用已有证据。扫描器能力不足用
`advisory` 报告;本次修改缺少现行契约要求的验证用 `not_yet_proven`,明确违规用
`violated`,后二者阻断批准并给出契约、触发修改、观察证据、最小修复与复验命令。
其他仓库使用自己的 CI 与契约规则,不能继承 LoopX 的检查名。

A matched model evaluation should preselect ordinary edits, legal contract
extensions, and real contract defects; fix tasks, model/version, tools, seeds
where supported, and total budgets across policies. Count review and repair
within that budget. Compare independently accepted completions, tokens, elapsed
time, false blocks and missed defects; report uncertainty and repeat stochastic
runs. Deterministic consistency fixtures establish the boundary, not model
benchmark uplift or non-regression.

The per-actionable-PR `pull_request_review_plan_v1` records the exact target,
applicability, required evidence ids, and an initially `unverified`
`pull_request_review_result_v1` skeleton. Metadata, labels, file counts, risk
Expand Down
Loading