






























@@ -24,40 +24,47 @@ docsRefs:
2424 - docs/help/testing.md
2525codeRefs:
2626 - src/crestodian/operations.ts
27+ - scripts/e2e/crestodian-first-run-spec.json
2728 - scripts/e2e/crestodian-first-run-docker-client.ts
2829 - extensions/qa-lab/src/suite-runtime-agent-process.ts
2930execution:
3031kind: flow
3132summary: Drive the public Crestodian CLI in an isolated fresh state dir and verify setup/model/agent/Discord/audit results.
3233config:
33-stateDirName: crestodian-ring-zero-state
34-defaultWorkspaceName: crestodian-main-workspace
35-agentWorkspaceName: crestodian-reef-workspace
36-agentId: reef
37-model: openai/gpt-5.2
38-discordEnv: DISCORD_BOT_TOKEN
39-discordToken: openclaw-crestodian-qa-discord-token
34+specPath: scripts/e2e/crestodian-first-run-spec.json
4035```
41364237```yaml qa-flow
4338steps:
4439 - name: bootstraps config through Crestodian CLI
4540actions:
41+ - set: setupSpec
42+value:
43+expr: "JSON.parse(await fs.readFile(path.join(env.repoRoot, config.specPath), 'utf8'))"
4644 - set: stateDir
4745value:
48-expr: "path.join(env.gateway.tempRoot, config.stateDirName)"
46+expr: "path.join(env.gateway.tempRoot, setupSpec.stateDirName)"
4947 - set: configPath
5048value:
5149expr: "path.join(stateDir, 'openclaw.json')"
5250 - set: defaultWorkspace
5351value:
54-expr: "path.join(env.gateway.tempRoot, config.defaultWorkspaceName)"
52+expr: "path.join(env.gateway.tempRoot, setupSpec.defaultWorkspaceName)"
5553 - set: agentWorkspace
5654value:
57-expr: "path.join(env.gateway.tempRoot, config.agentWorkspaceName)"
55+expr: "path.join(env.gateway.tempRoot, setupSpec.agentWorkspaceName)"
56+ - set: commandVars
57+value:
58+expr: "({ defaultWorkspace, agentWorkspace, agentId: setupSpec.agentId, model: setupSpec.model, discordEnv: setupSpec.discordEnv })"
59+ - set: renderCommand
60+value:
61+lambda:
62+params:
63+ - template
64+expr: "String(template).replace(/\\{([A-Za-z0-9_]+)\\}/g, (match, key) => String(commandVars[key] ?? match))"
5865 - set: crestodianEnv
5966value:
60-expr: "({ OPENCLAW_STATE_DIR: stateDir, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(env.repoRoot, 'dist', 'extensions'), [config.discordEnv]: config.discordToken })"
67+expr: "({ OPENCLAW_STATE_DIR: stateDir, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(env.repoRoot, 'dist', 'extensions'), [setupSpec.discordEnv]: setupSpec.discordToken })"
6168 - call: fs.rm
6269args:
6370 - ref: stateDir
@@ -85,141 +92,39 @@ steps:
8592expr: 'String(overviewOutput).includes(''Next: run "setup" to create a starter config'')'
8693message:
8794expr: "`fresh Crestodian overview did not recommend setup: ${overviewOutput}`"
88- - call: runQaCli
89-saveAs: setupOutput
90-args:
91- - ref: env
92- - - crestodian
93- - --yes
94- - -m
95- - expr: "`setup workspace ${defaultWorkspace} model ${config.model}`"
96- - timeoutMs: 60000
97-env:
98-ref: crestodianEnv
99- - assert:
100-expr: "String(setupOutput).includes('[crestodian] done: crestodian.setup')"
101-message:
102-expr: "`Crestodian setup did not apply: ${setupOutput}`"
103- - call: runQaCli
104-saveAs: modelOutput
105-args:
106- - ref: env
107- - - crestodian
108- - --yes
109- - -m
110- - expr: "`set default model ${config.model}`"
111- - timeoutMs: 60000
112-env:
113-ref: crestodianEnv
114- - assert:
115-expr: "String(modelOutput).includes('[crestodian] done: config.setDefaultModel')"
116-message:
117-expr: "`Crestodian model update did not apply: ${modelOutput}`"
118- - call: runQaCli
119-saveAs: agentOutput
120-args:
121- - ref: env
122- - - crestodian
123- - --yes
124- - -m
125- - expr: "`create agent ${config.agentId} workspace ${agentWorkspace} model ${config.model}`"
126- - timeoutMs: 60000
127-env:
128-ref: crestodianEnv
129- - assert:
130-expr: "String(agentOutput).includes('[crestodian] done: agents.create')"
131-message:
132-expr: "`Crestodian agent creation did not apply: ${agentOutput}`"
133- - call: runQaCli
134-saveAs: discordPluginAllowOutput
135-args:
136- - ref: env
137- - - crestodian
138- - --yes
139- - -m
140- - config set plugins.allow ["discord"]
141- - timeoutMs: 60000
142-env:
143-ref: crestodianEnv
144- - assert:
145-expr: "String(discordPluginAllowOutput).includes('[crestodian] done: config.set')"
146-message:
147-expr: "`Crestodian Discord plugin allowlist did not apply: ${discordPluginAllowOutput}`"
148- - call: runQaCli
149-saveAs: discordPluginEntryOutput
150-args:
151- - ref: env
152- - - crestodian
153- - --yes
154- - -m
155- - config set plugins.entries.discord.enabled true
156- - timeoutMs: 60000
157-env:
158-ref: crestodianEnv
159- - assert:
160-expr: "String(discordPluginEntryOutput).includes('[crestodian] done: config.set')"
161-message:
162-expr: "`Crestodian Discord plugin entry did not apply: ${discordPluginEntryOutput}`"
163- - call: runQaCli
164-saveAs: discordTokenOutput
165-args:
166- - ref: env
167- - - crestodian
168- - --yes
169- - -m
170- - expr: "`config set-ref channels.discord.token env ${config.discordEnv}`"
171- - timeoutMs: 60000
172-env:
173-ref: crestodianEnv
174- - assert:
175-expr: "String(discordTokenOutput).includes('[crestodian] done: config.setRef')"
176-message:
177-expr: "`Crestodian Discord SecretRef did not apply: ${discordTokenOutput}`"
178- - call: runQaCli
179-saveAs: discordEnabledOutput
180-args:
181- - ref: env
182- - - crestodian
183- - --yes
184- - -m
185- - config set channels.discord.enabled true
186- - timeoutMs: 60000
187-env:
188-ref: crestodianEnv
189- - assert:
190-expr: "String(discordEnabledOutput).includes('[crestodian] done: config.set')"
191-message:
192-expr: "`Crestodian Discord enable did not apply: ${discordEnabledOutput}`"
193- - call: runQaCli
194-saveAs: validationOutput
195-args:
196- - ref: env
197- - - crestodian
198- - -m
199- - validate config
200- - timeoutMs: 60000
201-env:
202-ref: crestodianEnv
203- - assert:
204-expr: "String(validationOutput).includes('Config valid:')"
205-message:
206-expr: "`Crestodian config validation did not pass: ${validationOutput}`"
95+ - forEach:
96+items:
97+ref: setupSpec.commands
98+item: commandStep
99+actions:
100+ - call: runQaCli
101+saveAs: commandOutput
102+args:
103+ - ref: env
104+ - expr: "['crestodian', ...(commandStep.approve ? ['--yes'] : []), '-m', renderCommand(commandStep.message)]"
105+ - timeoutMs: 60000
106+env:
107+ref: crestodianEnv
108+ - assert:
109+expr: "String(commandOutput).includes(commandStep.expectOutput)"
110+message:
111+expr: "`Crestodian command ${commandStep.id} did not produce ${commandStep.expectOutput}: ${commandOutput}`"
207112 - set: writtenConfig
208113value:
209114expr: "JSON.parse(await fs.readFile(configPath, 'utf8'))"
210115 - set: agent
211116value:
212-expr: "writtenConfig.agents?.list?.find((candidate) => candidate.id === config.agentId)"
117+expr: "writtenConfig.agents?.list?.find((candidate) => candidate.id === setupSpec.agentId)"
213118 - assert:
214119expr: "writtenConfig.agents?.defaults?.workspace === defaultWorkspace"
215120message:
216121expr: "`default workspace mismatch: ${JSON.stringify(writtenConfig.agents?.defaults)}`"
217122 - assert:
218-expr: "writtenConfig.agents?.defaults?.model?.primary === config.model"
123+expr: "writtenConfig.agents?.defaults?.model?.primary === setupSpec.model"
219124message:
220125expr: "`default model mismatch: ${JSON.stringify(writtenConfig.agents?.defaults?.model)}`"
221126 - assert:
222-expr: "agent?.workspace === agentWorkspace && agent?.model === config.model"
127+expr: "agent?.workspace === agentWorkspace && agent?.model === setupSpec.model"
223128message:
224129expr: "`agent config mismatch: ${JSON.stringify(agent)}`"
225130 - assert:
@@ -231,22 +136,18 @@ steps:
231136message:
232137expr: "`Discord was not enabled: ${JSON.stringify(writtenConfig.channels?.discord)}`"
233138 - assert:
234-expr: "writtenConfig.channels?.discord?.token?.source === 'env' && writtenConfig.channels?.discord?.token?.id === config.discordEnv"
139+expr: "writtenConfig.channels?.discord?.token?.source === 'env' && writtenConfig.channels?.discord?.token?.id === setupSpec.discordEnv"
235140message:
236141expr: "`Discord token was not an env SecretRef: ${JSON.stringify(writtenConfig.channels?.discord?.token)}`"
237142 - assert:
238-expr: "!JSON.stringify(writtenConfig.channels?.discord ?? {}).includes(config.discordToken)"
143+expr: "!JSON.stringify(writtenConfig.channels?.discord ?? {}).includes(setupSpec.discordToken)"
239144message: Crestodian persisted the raw Discord token.
240145 - set: auditText
241146value:
242147expr: "await fs.readFile(path.join(stateDir, 'audit', 'crestodian.jsonl'), 'utf8')"
243148 - forEach:
244149items:
245- - crestodian.setup
246- - config.setDefaultModel
247- - agents.create
248- - config.setRef
249- - config.set
150+ref: setupSpec.auditOperations
250151item: operation
251152actions:
252153 - assert:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。