feat: release Dever 0.1.4 with enforceable project guidance

This commit is contained in:
Antigravity committed 2026-10-09 05:33:57 +00:00
1 parent 201bdee372
commit 89629c2197
86 files changed
+2798 -338

No files matched your search

+4 -2
View File
@@ -68,11 +68,13 @@ python3 -B test/ci/run.py run --stage sandbox
python3 -B test/ci/run.py run --stage bounded-perf
```
以上命令由同一受限托管进程串行执行。若 author 改变输入,应在作者准备完成后再跑正式静态/default/all-feature门;较早输入版本的证据会显示过期。每阶段写入新的独占时间目录,开始即记录 running;缺输入记录 blocked,失败命令保留 exit/count/日志与原始异常。`summary` 与 inventory 消费者只接受最新一次尝试的当前源码/输入证据,不能越过较新的失败或中断回用旧 pass;所有必选阶段通过才返回0。源码摘要覆盖未提交文件,不只看 Git HEAD。
以上命令由同一受限托管进程串行执行。若 author 改变输入,应在作者准备完成后再跑正式静态/default/all-feature门;较早输入版本的证据会显示过期。每阶段写入新的独占时间目录,开始即记录 running;缺输入记录 blocked,失败命令保留 exit/count/日志与原始异常。`summary` 与 inventory 消费者只接受最新一次尝试的当前源码/输入证据,不能越过较新的失败或中断回用旧 pass;所有必选阶段通过才返回0。源码摘要覆盖未提交文件及 `skills/` 的指引、模板和示例,不只看 Git HEAD;修改或删除这些文件会使旧结果失效。
default/all-features 由实际 Cargo metadata 注册 target 和已解析 feature 形成串行矩阵,覆盖 lib/bin/integration/doctest;default 显式保留 workspace 解析的 package features,最小 feature 阶段另按单 package 验证。只有显式启用 `remove_test_executables` 时才删除 Cargo 本次返回、`profile.test=true` 且位于 target 下 `deps/` 的测试可执行文件,失败阶段同样清理;不删除作者工具、静态 init、依赖 archive 或整个 target。删除对象在日志 JSON 中可审计,重跑 Cargo 可恢复。inventory 按 package/target/name 三元组核对完整性,跨 target 同名用例不能代替缺失项。
Python 阶段包含 `test/ci`、`test/performance`、`test/ecosystem-release` 三个目录;后两者必须已配置 `performance.network_peer/live_peer`,真实自有 loopback检查由主代理执行。三 SDK 路径全部必需,三个明确成功标志全部出现才通过。
Python 阶段包含 `test/ci`、`test/performance`、`test/ecosystem-release` 三个目录,并执行 `test/skill_examples.py`,检查 skill 安装后的本地链接、文件预算、标准库签名和教程代码与源码的一致性。教程代码块须链接到受检查项目中的唯一源码片段;文档或源码漂移都会失败。performance 测试必须已配置 `performance.network_peer/live_peer`,真实自有 loopback 检查由主代理执行。三 SDK 路径全部必需,三个明确成功标志全部出现才通过。
只验证本次 CI 或 skill 改动时,可运行 `python3 -B test/ci/test_gate.py -v` 和 `python3 -B test/skill_examples.py -v`,不需要启动完整 CI、数据库或压力测试。
PostgreSQL 只接受显式 loopback端口和恰好一个 `{case}` 库名模板,拒绝 query 覆盖 host/dbname 等。增加 `performance.linux_ci.owned_postgres: {"data_directory":"/absolute/owned-cluster","port":51234}`,由托管者创建所需基库并在 finally 停止该自有实例;gate复用现有用例的 schema/tenant 清理,不替用户启动或连接既有业务数据库。
+3 -2
View File
@@ -20,7 +20,7 @@ STAGES = ("static", "default", "all-features", "features", "python", "sdk",
"inventory", "author", "native", "postgres", "sandbox", "bounded-perf",
"cms-64", "cms-128", "http2-64", "http2-128", "recovery-64", "recovery-128",
"live-64", "live-128")
SOURCE_ROOTS = ("crates", "test", "sdk", "library", "examples", ".cargo")
SOURCE_ROOTS = ("crates", "test", "sdk", "library", "examples", "skills", ".cargo")
class MissingInput(Exception):
@@ -129,7 +129,8 @@ def command_plan(stage, config):
python = str(absolute_file(tools.get("python"), "performance.linux_ci.tools.python"))
if stage == "python":
return [[python, "-B", "-m", "unittest", "discover", "-s", directory, "-p", "test_*.py", "-v"]
for directory in ("test/ci", "test/performance", "test/ecosystem-release")]
for directory in ("test/ci", "test/performance", "test/ecosystem-release")] + [
[python, "-B", "test/skill_examples.py", "-v"]]
if stage == "sdk":
return [[python, "-B", "test/external-sdk/check.py", "--python", python,
"--node", str(absolute_file(tools.get("node"), "performance.linux_ci.tools.node")),
+76
View File
@@ -5,6 +5,7 @@ import contextlib
import io
import json
from pathlib import Path
import shutil
import sys
import tempfile
import unittest
@@ -177,6 +178,81 @@ class GateTests(unittest.TestCase):
(root / "Cargo.lock").write_text("changed immutable input")
self.assertNotEqual(author_before, gate.input_fingerprint({}, config, "author"))
def test_skill_sources_invalidate_evidence_but_generated_caches_do_not(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
for name in ("Cargo.toml", "Cargo.lock"):
(root / name).write_text(name)
sources = ("skills/assets/template/AGENTS.md", "skills/references/development.md",
"skills/assets/examples/jobs/module/report/archive/app.dever")
for name in sources:
path = root / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("original")
original = gate.source_fingerprint(root)
for name in sources:
path = root / name
with self.subTest(source=name):
path.write_text("changed")
self.assertNotEqual(original, gate.source_fingerprint(root))
path.unlink()
self.assertNotEqual(original, gate.source_fingerprint(root))
path.write_text("original")
self.assertEqual(original, gate.source_fingerprint(root))
for cache in ("__pycache__", "target", ".git"):
path = root / "skills/assets/examples/jobs" / cache / "generated"
path.parent.mkdir()
path.write_text("generated bytes")
self.assertEqual(original, gate.source_fingerprint(root))
path.write_text("changed generated bytes")
self.assertEqual(original, gate.source_fingerprint(root))
path.unlink()
self.assertEqual(original, gate.source_fingerprint(root))
def test_python_plan_runs_real_skill_checks_and_rejects_each_failure(self):
config = {"tools": {"cargo": sys.executable, "python": sys.executable}}
commands = gate.command_plan("python", config)
skill_commands = [command for command in commands if "test/skill_examples.py" in command]
self.assertEqual(len(skill_commands), 1)
command = skill_commands[0]
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
(root / "test").mkdir()
shutil.copyfile(gate.ROOT / "test/skill_examples.py", root / "test/skill_examples.py")
for name in ("skills", "library"):
shutil.copytree(gate.ROOT / name, root / name)
logs = root / "logs"
logs.mkdir()
with patch.object(gate, "ROOT", root):
record, content = gate.run_command(command, logs, 0, 10)
gate.check_output("python", record, content)
test_count = record["python_tests"]
self.assertGreater(test_count, 0)
for number, (relative, broken, case) in enumerate((
("skills/SKILL.md", "[broken](missing.md)",
"test_relocated_skill_links_and_release_file_limits"),
("skills/references/signatures.md", "```signatures\ndever.io.missing() ()\n```\n",
"test_signature_index_matches_public_library_declarations"),
("skills/references/language.md",
(root / "skills/references/language.md").read_text().replace(
'"Hello, "', '"Incorrect tutorial, "', 1
),
"test_tutorial_code_matches_relocated_checked_sources"),
), 1):
path = root / relative
original = path.read_bytes()
path.write_text(broken)
with self.subTest(check=case), self.assertRaisesRegex(RuntimeError, "command exited 1"):
gate.run_command(command, logs, number, 10)
result = json.loads((logs / f"{number:03d}.command.json").read_text())
self.assertEqual(result["exit_code"], 1)
self.assertEqual(result["python_tests"], test_count)
log = (logs / f"{number:03d}.log").read_text()
self.assertTrue(
f"FAIL: {case}" in log or f"ERROR: {case}" in log, log
)
path.write_bytes(original)
def test_cleanup_cannot_delete_author_binary(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)