docs(comments): 注释中文化 · internal/runtime/orchestration(29 文件,纯替换)
- 只翻注释/文档字符串:剥离注释后代码行逐字节不变(机械校验只剩允许的行尾注释形态) - 裁决编号与 design 节号(02.4.3 / 11.4.2 / design/05 §9 等)逐字保留 - 复核:go build 绿;orchestration 及其子包(audit/comm)测试绿;gofmt 干净
This commit is contained in:
1 parent
fe90f64a56
commit
eb1828ee7a
29 files changed
+751
-949
No files matched your search
@@ -14,17 +14,14 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||||
)
|
||||
|
||||
// This file locks the CONTRACT half of design/22 §8-2 (the behavior half lives in
|
||||
// spawnpersona_test.go): the tool schema and result shape a model sees, the rejection
|
||||
// of an unknown role, and the session env a sub-agent run is stamped with. The point of
|
||||
// these tests is that a dispatch which names no persona stays byte-identical to the
|
||||
// pre-change behavior, while a dispatch that names an unusable role fails loudly rather
|
||||
// than degrading into the default role.
|
||||
// 本文件锁定 design/22 §8-2 的契约那一半(行为那一半在 spawnpersona_test.go):模型
|
||||
// 看到的工具 schema 与结果形状、未知角色的拒绝,以及子代理运行被盖上的会话 env。这些
|
||||
// 测试的重点是:不指名 persona 的派发与改动前的行为逐字节一致,而指名一个不可用角色的
|
||||
// 派发会明确报错,而不是降级为默认角色。
|
||||
|
||||
// TestSpawnPersonaRejectsUnknownRole is the "未知角色 → 明确报错,不静默回落" half: an id
|
||||
// the process cannot run is refused before anything starts, for a single dispatch and
|
||||
// for a batch (all-or-nothing, so no half-run batch is left behind), and a blank id is
|
||||
// never treated as a role.
|
||||
// TestSpawnPersonaRejectsUnknownRole 是「未知角色 → 明确报错,不静默回落」那一半:本
|
||||
// 进程跑不了的 id 会在任何东西启动之前就被拒绝,单次派发与批次都是如此(全有或全无,
|
||||
// 因此不会留下半跑的批次);空 id 也绝不会被当作角色。
|
||||
func TestSpawnPersonaRejectsUnknownRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
@@ -84,10 +81,9 @@ func TestSpawnPersonaRejectsUnknownRole(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSchedulerSpawnCarriesPersonaEnv pins the library half of the same contract for
|
||||
// callers that bypass the tool: Spawn stamps the target role onto the run's env (so
|
||||
// the loop resolves that role's prompt and memory scope), a blank persona leaves the
|
||||
// inherited env untouched, and Spawn itself refuses an unknown role.
|
||||
// TestSchedulerSpawnCarriesPersonaEnv 为绕过该工具的调用方钉住同一契约的库那一半:
|
||||
// Spawn 把目标角色盖到运行的 env 上(使循环解析该角色的提示词与记忆作用域),空 persona
|
||||
// 让继承来的 env 原样不动,Spawn 自身也拒绝未知角色。
|
||||
func TestSchedulerSpawnCarriesPersonaEnv(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newScheduler(t, Options{PersonaExists: func(id string) bool { return id == targetRole }})
|
||||
@@ -98,8 +94,8 @@ func TestSchedulerSpawnCarriesPersonaEnv(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Spawn: %v", err)
|
||||
}
|
||||
// The recorded binding names the target role too, so the progress events and the
|
||||
// ws subagent_event frame report the role the sub-agent answered as.
|
||||
// 记录下来的绑定也点名目标角色,因此进度事件与 ws subagent_event 帧报告的是子代理
|
||||
// 以之作答的角色。
|
||||
if sub.RoleID != targetRole || sub.PersonaID != targetRole {
|
||||
t.Fatalf("sub-agent binding = %q/%q, want the target role on both", sub.RoleID, sub.PersonaID)
|
||||
}
|
||||
@@ -126,9 +122,8 @@ func TestSchedulerSpawnCarriesPersonaEnv(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// envRunner records the session env each sub-agent run starts with. The memory scope
|
||||
// a run uses is decided from exactly this value, so it is the boundary under test for
|
||||
// callers that go through Scheduler.Spawn directly.
|
||||
// envRunner 记录每次子代理运行起步时的会话 env。一次运行使用哪个记忆作用域正是由这个值
|
||||
// 决定的,因此对直接走 Scheduler.Spawn 的调用方而言,它就是被测边界。
|
||||
type envRunner struct {
|
||||
envs chan loop.SessionEnv
|
||||
}
|
||||
@@ -138,10 +133,9 @@ func (r envRunner) Run(ctx context.Context, _ string, _ []model.Message) (*loop.
|
||||
return &loop.Result{Text: "ok", Steps: 1}, nil
|
||||
}
|
||||
|
||||
// TestSpawnToolResultShapeIsUnchangedWithoutPersona pins the strict backward
|
||||
// compatibility of the model-visible result: a dispatch that names no persona still
|
||||
// returns exactly the same JSON keys as before this feature — no "persona" field is
|
||||
// added anywhere — so nothing about the omitted-persona contract moved.
|
||||
// TestSpawnToolResultShapeIsUnchangedWithoutPersona 钉住模型可见结果的严格向后兼容:
|
||||
// 不指名 persona 的派发仍返回与引入本功能之前完全相同的 JSON 键 —— 任何地方都没有新增
|
||||
// "persona" 字段 —— 因此省略 persona 的契约一点没动。
|
||||
func TestSpawnToolResultShapeIsUnchangedWithoutPersona(t *testing.T) {
|
||||
t.Parallel()
|
||||
s, sp := spawnToolFor(t, Options{}, time.Second)
|
||||
@@ -175,7 +169,7 @@ func TestSpawnToolResultShapeIsUnchangedWithoutPersona(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// sortedKeys returns m's keys in sorted order, for a stable comparison in tests.
|
||||
// sortedKeys 按字典序返回 m 的键,供测试做稳定比较。
|
||||
func sortedKeys(m map[string]any) []string {
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
@@ -185,17 +179,16 @@ func sortedKeys(m map[string]any) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// TestSpawnSchemaKeepsPersonaOptional pins the tool contract: persona is an optional
|
||||
// string at both the single-goal and the tasks level, nothing new is required, and the
|
||||
// pre-existing arguments keep their meaning — so a model (or an old client) that never
|
||||
// sends persona sees byte-identical validation behavior.
|
||||
// TestSpawnSchemaKeepsPersonaOptional 钉住工具契约:persona 在单 goal 与 tasks 两级都是
|
||||
// 可选字符串,没有新增任何必填项,既有参数含义不变 —— 因此从不发 persona 的模型(或旧
|
||||
// 客户端)看到的校验行为逐字节相同。
|
||||
func TestSpawnSchemaKeepsPersonaOptional(t *testing.T) {
|
||||
t.Parallel()
|
||||
props, err := NewSpawnTool(nil).Parameters()
|
||||
if err != nil {
|
||||
t.Fatalf("Parameters: %v", err)
|
||||
}
|
||||
// The required set is unchanged: goal or tasks, nothing else.
|
||||
// 必填集合未变:goal 或 tasks,别无其他。
|
||||
if got := requiredAnyOf(t, props); strings.Join(got, ",") != "goal,tasks" {
|
||||
t.Fatalf("anyOf required = %v, want goal/tasks only", got)
|
||||
}
|
||||
@@ -216,7 +209,7 @@ func TestSpawnSchemaKeepsPersonaOptional(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// requiredAnyOf flattens the anyOf required lists of a tool schema.
|
||||
// requiredAnyOf 摊平工具 schema 的各个 anyOf required 列表。
|
||||
func requiredAnyOf(t *testing.T, props map[string]any) []string {
|
||||
t.Helper()
|
||||
anyOf, _ := props["anyOf"].([]any)
|
||||
@@ -232,7 +225,7 @@ func requiredAnyOf(t *testing.T, props map[string]any) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// stringProp returns the named top-level property of a tool schema property block.
|
||||
// stringProp 返回工具 schema 属性块里指名的顶层属性。
|
||||
func stringProp(t *testing.T, props map[string]any, name string) map[string]any {
|
||||
t.Helper()
|
||||
properties, _ := props["properties"].(map[string]any)
|
||||
|
||||
Reference in new issue
Block a user