@senpi/runtime 3.0.9-dev.feat-strategy-compos.20260716013009 → 3.0.9-dev.feat-strategy-compos.20260716233548

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cli/composer-commands.d.ts.map +1 -1
  2. package/dist/cli/composer-commands.js +111 -2
  3. package/dist/cli/composer-commands.js.map +1 -1
  4. package/dist/cli/validate-runtime-config.mjs +66 -0
  5. package/dist/composer/AUTHORING.md +224 -22
  6. package/dist/composer/authoring/__init__.py +11 -1
  7. package/dist/composer/authoring/answers.py +72 -1
  8. package/dist/composer/authoring/archetypes/__init__.py +6 -5
  9. package/dist/composer/authoring/archetypes/_common.py +193 -1
  10. package/dist/composer/authoring/archetypes/breakout.py +134 -0
  11. package/dist/composer/authoring/archetypes/skeleton.py +23 -1
  12. package/dist/composer/authoring/archetypes/trend_momentum.py +31 -3
  13. package/dist/composer/cli.py +137 -5
  14. package/dist/composer/compiler/compile.py +202 -13
  15. package/dist/composer/generated/catalog.md +36 -2
  16. package/dist/composer/generated/describe.md +465 -0
  17. package/dist/composer/generated/graph.schema.json +748 -5
  18. package/dist/composer/primitives/__init__.py +81 -0
  19. package/dist/composer/primitives/adapter.py +243 -0
  20. package/dist/composer/primitives/indicators.py +124 -1
  21. package/dist/composer/primitives/sources.py +589 -15
  22. package/dist/composer/primitives/stateful.py +217 -15
  23. package/dist/composer/registry/generate.py +11 -8
  24. package/dist/composer/registry/mcp-tools-snapshot.json +193 -0
  25. package/dist/composer/registry/mcp_closure.py +311 -0
  26. package/dist/composer/registry/models.py +88 -2
  27. package/dist/composer/registry/nodes.py +634 -10
  28. package/dist/composer/scaffold_path.py +118 -0
  29. package/dist/composer/templates/scan.py.j2 +5 -2
  30. package/dist/composer/validator/errors.py +36 -0
  31. package/dist/composer/validator/graph_checks.py +97 -2
  32. package/dist/composer/vendoring.py +103 -0
  33. package/dist/composer/verbs/__init__.py +17 -1
  34. package/dist/composer/verbs/_box.py +308 -0
  35. package/dist/composer/verbs/check.py +61 -0
  36. package/dist/composer/verbs/close.py +494 -0
  37. package/dist/composer/verbs/deploy.py +65 -9
  38. package/dist/composer/verbs/fund.py +498 -0
  39. package/dist/composer/verbs/install.py +586 -0
  40. package/dist/composer/verbs/runtime_validate.py +162 -2
  41. package/dist/composer/verbs/smoke.py +44 -25
  42. package/dist/composer/verbs/status.py +205 -27
  43. package/dist/runtime/load-runtime-config.d.ts +17 -0
  44. package/dist/runtime/load-runtime-config.d.ts.map +1 -1
  45. package/dist/runtime/load-runtime-config.js +2 -2
  46. package/dist/runtime/load-runtime-config.js.map +1 -1
  47. package/dist/skills-manager/manifest.d.ts.map +1 -1
  48. package/dist/skills-manager/manifest.js +0 -2
  49. package/dist/skills-manager/manifest.js.map +1 -1
  50. package/dist/skills-manager/skills-manifest.json +0 -2
  51. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"composer-commands.d.ts","sourceRoot":"","sources":["../../src/cli/composer-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,UAAU,gBAAgB;IACxB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACvD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC5C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC9D,MAAM,CACJ,EAAE,EACE,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GACzD,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GACzE,gBAAgB,CAAC;CACrB;AAYD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AA6FD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAoKjE"}
1
+ {"version":3,"file":"composer-commands.d.ts","sourceRoot":"","sources":["../../src/cli/composer-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,UAAU,gBAAgB;IACxB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACvD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC5C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC9D,MAAM,CACJ,EAAE,EACE,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GACzD,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GACzE,gBAAgB,CAAC;CACrB;AAYD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AAmGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CA8QjE"}
@@ -55,6 +55,12 @@ function detectEnvError(pythonBin, root, result) {
55
55
  if (mod === "composer" || mod.startsWith("composer.")) {
56
56
  return `composer: the 'composer' package was not found under ${root} — on a claw run \`npm run build\` (copy:composer) or set COMPOSER_ROOT to your checkout root.`;
57
57
  }
58
+ if (mod === "scaffold" || mod.startsWith("scaffold.")) {
59
+ // The scaffold harness ships WITH the runtime (copy:external-scanners), never from PyPI.
60
+ // Do NOT route this to the "pip install deps" hint — and warn off `pip install scaffold`
61
+ // (an unrelated PyPI package), the exact dead end the original dev-box run hit.
62
+ return `composer: the 'scaffold' harness package was not found — on a claw run \`npm run build\` (copy:external-scanners stages it into dist/scanners/external) or set COMPOSER_SCAFFOLD_DIR to the dir that CONTAINS scaffold/ (…/src/scanners/external). Do NOT \`pip install scaffold\` — that is an unrelated PyPI package.`;
63
+ }
58
64
  return `composer: Python dependency '${mod}' is missing — install the composer deps: ${requirementsHint(root)}`;
59
65
  }
60
66
  return null;
@@ -117,7 +123,7 @@ function printErrors(errors) {
117
123
  }
118
124
  export function registerComposerCli(senpi) {
119
125
  const composer = senpi.command("composer");
120
- composer.description("Strategy Composer: new / validate / compile / check / deploy / status a strategy graph; catalog / describe nodes");
126
+ composer.description("Strategy Composer: new / validate / compile / check / deploy / fund / install / close / status a strategy graph; catalog / describe nodes");
121
127
  // new / validate / compile stream Python's own (rich) human render through — teaching
122
128
  // errors, gap reports, emitted scan.py — forwarding only the relevant flags. Thin: no
123
129
  // TS-side interpretation; the exit code is Python's (0 ok, 2 gap report, 3 clobber-refused).
@@ -239,20 +245,123 @@ export function registerComposerCli(senpi) {
239
245
  printErrors(check.errors ?? []);
240
246
  }
241
247
  else {
248
+ // Always surface the ABSOLUTE staged dir Python resolved (a relative `-o` against an
249
+ // unexpected cwd was "deploy exited 0 but there are no files where I looked"). UNCHANGED
250
+ // says plainly that nothing was rewritten so an idempotent no-op never reads as a failure.
242
251
  const staged = (json.staged ?? {});
243
- console.log(`${verdict}: ${graph} — staged to ${staged.dir}. (live tick: slice 8)`);
252
+ const dir = staged.dir ?? "(no staged dir reported)";
253
+ if (verdict === "UNCHANGED") {
254
+ console.log(`UNCHANGED: ${graph} — already staged at ${dir}, nothing rewritten (idempotent no-op).`);
255
+ }
256
+ else {
257
+ console.log(`${verdict}: ${graph} — staged to ${dir}.`);
258
+ }
259
+ }
260
+ process.exitCode = status;
261
+ });
262
+ composer
263
+ .command("fund <target>")
264
+ .description("Create + fund the strategy's wallet (MCP strategy_create_custom_strategy): preflights the balance, waits for ACTIVE, safe to re-run while funding settles")
265
+ .option("--budget <usd>", "Explicit USD to fund this strategy's wallet with (>= $100; never derived/split)")
266
+ .option("-o, --out <dir>", "Staging dir (when target is a graph file, not a staged dir)")
267
+ .option("--state-dir <dir>", "Base senpi-state dir (default: $SENPI_STATE_DIR or ~/.openclaw/senpi-state)")
268
+ .option("--json", "Print the raw Python fund verdict JSON")
269
+ .action(async (target, opts) => {
270
+ const args = ["fund", target];
271
+ if (opts.budget !== undefined)
272
+ args.push("--budget", String(opts.budget));
273
+ if (opts.out)
274
+ args.push("--out", opts.out);
275
+ if (opts.stateDir)
276
+ args.push("--state-dir", opts.stateDir);
277
+ // The rim owns the wall-clock: created_at is injected as an ISO string (like install's bound-at).
278
+ args.push("--created-at", new Date().toISOString());
279
+ if (opts.json)
280
+ args.push("--json");
281
+ const { status, stdout, stderr, envError } = runComposer(args);
282
+ if (envError) {
283
+ process.exitCode = 1;
284
+ return;
285
+ }
286
+ if (stdout)
287
+ process.stdout.write(stdout);
288
+ if (stderr)
289
+ process.stderr.write(stderr);
290
+ process.exitCode = status;
291
+ });
292
+ composer
293
+ .command("install <target>")
294
+ .description("Bind a staged unit onto THIS box: copy to a canonical hash dir, bind --wallet (or auto-bind a funded wallet.json), run `openclaw senpi runtime create`")
295
+ .option("--wallet <address>", "The strategy's ACTIVE 0x… wallet to bind (omit to auto-bind a funded wallet.json)")
296
+ .option("-o, --out <dir>", "Staging dir (when target is a graph file, not a staged dir)")
297
+ .option("--state-dir <dir>", "Base senpi-state dir (default: $SENPI_STATE_DIR or ~/.openclaw/senpi-state)")
298
+ .option("--runtime-id <id>", "Override the derived runtime id (default: <strategy>-<hash8>)")
299
+ .option("--json", "Print the raw Python install verdict JSON")
300
+ .action(async (target, opts) => {
301
+ const args = ["install", target];
302
+ if (opts.wallet)
303
+ args.push("--wallet", opts.wallet);
304
+ if (opts.out)
305
+ args.push("--out", opts.out);
306
+ if (opts.stateDir)
307
+ args.push("--state-dir", opts.stateDir);
308
+ if (opts.runtimeId)
309
+ args.push("--runtime-id", opts.runtimeId);
310
+ // Inject the wall-clock here (the rim owns the clock; the Python core stays deterministic).
311
+ args.push("--bound-at", new Date().toISOString());
312
+ if (opts.json)
313
+ args.push("--json");
314
+ const { status, stdout, stderr, envError } = runComposer(args);
315
+ if (envError) {
316
+ process.exitCode = 1;
317
+ return;
244
318
  }
319
+ if (stdout)
320
+ process.stdout.write(stdout);
321
+ if (stderr)
322
+ process.stderr.write(stderr);
323
+ process.exitCode = status;
324
+ });
325
+ composer
326
+ .command("close <target>")
327
+ .description("Safe teardown: stop the runtime, confirm gone, then trigger MCP strategy_close (flattens positions + returns funds). Idempotent — re-run to poll")
328
+ .option("--wallet <address>", "Close the strategy on this 0x… wallet directly")
329
+ .option("--strategy-id <id>", "Close this strategyId directly (legacy/ops-deployed strategies)")
330
+ .option("--state-dir <dir>", "Base senpi-state dir (default: $SENPI_STATE_DIR or ~/.openclaw/senpi-state)")
331
+ .option("--json", "Print the raw Python close verdict JSON")
332
+ .action(async (target, opts) => {
333
+ const args = ["close", target];
334
+ if (opts.wallet)
335
+ args.push("--wallet", opts.wallet);
336
+ if (opts.strategyId)
337
+ args.push("--strategy-id", opts.strategyId);
338
+ if (opts.stateDir)
339
+ args.push("--state-dir", opts.stateDir);
340
+ if (opts.json)
341
+ args.push("--json");
342
+ const { status, stdout, stderr, envError } = runComposer(args);
343
+ if (envError) {
344
+ process.exitCode = 1;
345
+ return;
346
+ }
347
+ if (stdout)
348
+ process.stdout.write(stdout);
349
+ if (stderr)
350
+ process.stderr.write(stderr);
245
351
  process.exitCode = status;
246
352
  });
247
353
  composer
248
354
  .command("status <target>")
249
355
  .description("Derived lifecycle position + the next verb to run (graph file or staging dir)")
250
356
  .option("-o, --out <dir>", "Staging directory to check for a deployed artifact")
357
+ .option("--state-dir <dir>", "Base senpi-state dir — extend status down the on-box install chain")
251
358
  .option("--json", "Print the raw Python status JSON")
252
359
  .action(async (target, opts) => {
253
360
  const args = ["status", target, "--json"];
254
361
  if (opts.out)
255
362
  args.push("--out", opts.out);
363
+ if (opts.stateDir)
364
+ args.push("--state-dir", opts.stateDir);
256
365
  const { status, stdout, json } = runComposer(args);
257
366
  if (opts.json || !json) {
258
367
  process.stdout.write(stdout);
@@ -1 +1 @@
1
- {"version":3,"file":"composer-commands.js","sourceRoot":"","sources":["../../src/cli/composer-commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"composer-commands.js","sourceRoot":"","sources":["../../src/cli/composer-commands.ts"],"names":[],"mappings":"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"}
@@ -46,10 +46,24 @@ async function main() {
46
46
 
47
47
  const imp = (rel) => import(pathToFileURL(pathJoin(distDir, rel)).href);
48
48
  let runtimeConfigSchema, parseDslPluginConfig, mapRuntimeExitToDslConfig, DslConfigValidationError;
49
+ // A3 (install-time parity): checks the runtime runs only when it actually boots — a
50
+ // runtime.yaml can pass the Zod schema + DSL preset parse yet still fail at install. Imported
51
+ // from dist alongside the existing three oracle objects so the same single source of truth
52
+ // (the runtime's own code) judges them. `validateDslPositionTracking` is Runtime.start()'s DSL
53
+ // wiring gate; `assertUnique*Names` + `assertActionPromptPlaceholdersResolvable` are
54
+ // loadRuntimeConfig's post-Zod asserts the rim otherwise skips (it avoids loadRuntimeConfig to
55
+ // keep structured errors). All throw plain Errors — caught below and mapped to distinct codes.
56
+ let validateDslPositionTracking,
57
+ assertActionPromptPlaceholdersResolvable,
58
+ assertUniqueScannerNames,
59
+ assertUniqueActionNames;
49
60
  try {
50
61
  ({ runtimeConfigSchema } = await imp("runtime/runtime-schema.js"));
51
62
  ({ parseDslPluginConfig, DslConfigValidationError } = await imp("dsl/config/index.js"));
52
63
  ({ mapRuntimeExitToDslConfig } = await imp("runtime/map-exit-config.js"));
64
+ ({ validateDslPositionTracking } = await imp("runtime/validate-dsl-position-tracking.js"));
65
+ ({ assertActionPromptPlaceholdersResolvable } = await imp("runtime/validate-action-prompts.js"));
66
+ ({ assertUniqueScannerNames, assertUniqueActionNames } = await imp("runtime/load-runtime-config.js"));
53
67
  } catch (err) {
54
68
  // Signal an ENV-absent condition distinctly so Python can skip (not RED) when dist is unbuilt.
55
69
  process.stdout.write(
@@ -117,6 +131,58 @@ async function main() {
117
131
  }
118
132
  }
119
133
 
134
+ // Third oracle layer (A3): install-time parity. The above proves the config PARSES; these
135
+ // reproduce the checks that only fire when the runtime BOOTS, so GREEN means "will install".
136
+ // Each new failure class carries a distinct machine `code` the Python CMP1xx mapper keys on.
137
+ // Collected together (not first-throw) so the author sees every install blocker at once.
138
+ const installErrors = [];
139
+
140
+ // load-time asserts loadRuntimeConfig runs post-Zod (the rim skips loadRuntimeConfig to keep
141
+ // structured errors, so it must re-run them here). Order matches load-runtime-config.ts.
142
+ try {
143
+ assertUniqueScannerNames(cfg);
144
+ } catch (err) {
145
+ installErrors.push({ path: "scanners", message: err?.message ?? String(err), code: "DUP_SCANNER_NAME" });
146
+ }
147
+ try {
148
+ assertUniqueActionNames(cfg);
149
+ } catch (err) {
150
+ installErrors.push({ path: "actions", message: err?.message ?? String(err), code: "DUP_ACTION_NAME" });
151
+ }
152
+ try {
153
+ assertActionPromptPlaceholdersResolvable(cfg);
154
+ } catch (err) {
155
+ installErrors.push({ path: "actions", message: err?.message ?? String(err), code: "UNRESOLVED_PROMPT_PLACEHOLDER" });
156
+ }
157
+
158
+ // DSL position-tracking wiring — the Runtime.start() gate (runtime.ts:452-457). hasDsl is
159
+ // derived exactly as the call site does: mapRuntimeExitToDslConfig(config.exit).dsl_preset
160
+ // present. The map call already succeeded in the DSL layer above whenever a dsl_preset exists;
161
+ // if it throws for a preset-less exit block, hasDsl is false (the gate is a no-op) — that class
162
+ // of error is surfaced by the schema/DSL layers, not misreported here.
163
+ try {
164
+ let hasDsl = false;
165
+ try {
166
+ const exitConfig = mapRuntimeExitToDslConfig(cfg.exit);
167
+ const dp = exitConfig?.dsl_preset;
168
+ hasDsl = dp != null && typeof dp === "object" && !Array.isArray(dp);
169
+ } catch {
170
+ hasDsl = false;
171
+ }
172
+ validateDslPositionTracking(cfg, hasDsl);
173
+ } catch (err) {
174
+ const message = err?.message ?? String(err);
175
+ // The single function throws one of three messages; the action-wiring ones point at actions.
176
+ const path = /action/i.test(message) ? "actions" : "scanners";
177
+ installErrors.push({ path, message, code: "DSL_POSITION_TRACKING" });
178
+ }
179
+
180
+ if (installErrors.length > 0) {
181
+ process.stdout.write(JSON.stringify({ ok: false, errors: installErrors }) + "\n");
182
+ process.exit(0);
183
+ return;
184
+ }
185
+
120
186
  process.stdout.write(JSON.stringify({ ok: true }) + "\n");
121
187
  process.exit(0);
122
188
  }
@@ -48,6 +48,16 @@ fixtures) or `composer/generated/` (registry-derived, do-not-edit).
48
48
  mkdir -p drafts
49
49
  ```
50
50
 
51
+ **`scaffold` resolves itself — never set a manual `PYTHONPATH` for it.** `check`/`deploy` run
52
+ the emitted `scan.py` under the runtime's `scaffold` harness. The composer finds `scaffold`
53
+ automatically in BOTH layouts — a dev checkout (`src/scanners/external`) and a claw dist install
54
+ (`dist/scanners/external`, no `src/` segment) — so you do not export any path. If it genuinely
55
+ cannot be found, the `smoke` stage fails RED with a teaching error naming the fix: on a claw run
56
+ `npm run build` (its `copy:external-scanners` step stages the harness), or set
57
+ `COMPOSER_SCAFFOLD_DIR` to the dir that CONTAINS `scaffold/`. Do **not** `pip install scaffold` —
58
+ that is an unrelated PyPI package and will not satisfy the import (the harness ships with the
59
+ runtime, never from PyPI).
60
+
51
61
  ## 3. Verb reference
52
62
 
53
63
  Every verb takes `--json` for machine-readable output; omit it for a human-readable
@@ -59,8 +69,11 @@ report.
59
69
  | `validate` | Schema + graph-semantic teaching-error report (exit 0 iff clean). | `... python -m composer.cli validate drafts/my_strat.graph.yaml` |
60
70
  | `check` | ONE verdict: validate + pure_fn inline tests + compile + one-tick smoke, staged so a failing stage skips the rest. | `... python -m composer.cli check drafts/my_strat.graph.yaml` |
61
71
  | `compile` | Materialize the graph into `scan.py` on the `external_scanner` contract. | `... python -m composer.cli compile drafts/my_strat.graph.yaml -o drafts/scan.py` |
62
- | `deploy` | Stage the COMPLETE standalone unit as a package tree (`strategy.yaml`, `main/runtime.yaml`, `main/scanners/scan.py`, `main/<id>.graph.yaml`, `manifest.json`, `deploy.json`); HARD-gated on a GREEN `check`. Does not install. | `... python -m composer.cli deploy drafts/my_strat.graph.yaml -o drafts/dist` |
63
- | `status` | Derives your lifecycle position (`absent` / `invalid` / `valid_undeployed` / `deployed_stale` / `deployed_current`) from the file + check + staged manifest, and names the next verb. No journal — the file is the state. | `... python -m composer.cli status drafts/my_strat.graph.yaml -o drafts/dist` |
72
+ | `deploy` | Stage the COMPLETE standalone unit as a package tree (`strategy.yaml`, `main/runtime.yaml`, `main/scanners/scan.py`, `main/scanners/strategy_primitives/` — the vendored primitives snapshot — `main/<id>.graph.yaml`, `manifest.json`, `deploy.json`); HARD-gated on a GREEN `check`. Does not install. The verdict always reports the ABSOLUTE staged dir (a relative `-o` resolves under your current working dir), and a re-deploy of unchanged bytes is `UNCHANGED` — "already staged at `<abs>`, nothing rewritten". | `... python -m composer.cli deploy drafts/my_strat.graph.yaml -o drafts/dist` |
73
+ | `fund` | Create + fund the strategy's ONE wallet via MCP `strategy_create_custom_strategy`. `--budget <usd>` is REQUIRED and EXPLICIT (≥ $100) — agree it with the user; it is NEVER derived or split. Preflights the visible balance (free Hyperliquid + EVM USDC), creates, records `wallet.json` BEFORE polling, then polls briefly to ACTIVE. Idempotent + safe to re-run while funds settle (re-run IS the poll): existing ACTIVE wallet → `FUNDED` no-op; still initializing → `PENDING`; stale → CMP231. Short balance → CMP232 (states the shortfall + the bridged-funds visibility lag). Teaching errors CMP230-233 (+ CMP223 for missing creds). | `... python -m composer.cli fund drafts/dist/my_strat --budget 250` |
74
+ | `install` | Bind a staged unit onto THIS box: preflight (manifest verify + GREEN oracle verdict) → copy to a content-addressed immutable dir `<senpi-state>/strategies/<id>/<hash8>/` (temp+verify+atomic-rename) → bind the wallet into the COPIED `runtime.yaml` (staged source stays pristine) → invoke the frozen `openclaw senpi runtime create -p …` consumer. Pass `--wallet 0x…` explicitly, OR omit it to AUTO-BIND the funded `wallet.json` from `composer fund` (only if MCP confirms the strategy is ACTIVE; explicit `--wallet` always wins). Failures map to teaching errors (CMP200-219, + CMP206 when there is no ACTIVE wallet to auto-bind). Resumable-by-artifact. | `... python -m composer.cli fund drafts/dist/my_strat --budget 250` then `... install drafts/dist/my_strat` |
75
+ | `close` | SAFE TEARDOWN of a running strategy: STOP the runtime first (`openclaw senpi runtime delete`), CONFIRM it is gone, and only THEN trigger MCP `strategy_close` (flattens all positions, returns funds). Idempotent — re-running IS the poll to `CLOSED`. Target is a strategy name / canonical hash dir / runtime id / `0x…` wallet / strategyId; on-box artifacts resolve first, else legacy `strategy_list` attribution (skillName / strategyId). Failures map to teaching errors (CMP220-225). | `... python -m composer.cli close my_strat --state-dir ~/.openclaw/senpi-state` |
76
+ | `status` | Derives your lifecycle position (`absent` / `invalid` / `valid_undeployed` / `deployed_stale` / `deployed_current`) from the file + check + staged manifest, and names the next verb. Pass `--state-dir` to EXTEND the chain onto a box: `installable` → `installed_unregistered` → `installed_stopped` → `installed_running`, distinguishing which hash is installed/running vs merely a prior landed version. Functional liveness (scan() has actually ticked) is reported honestly as NOT-checked — `runtime list` only proves the process is up. No journal — the file is the state. | `... python -m composer.cli status drafts/my_strat.graph.yaml -o drafts/dist --state-dir ~/.openclaw/senpi-state` |
64
77
  | `catalog` | The progressive-disclosure node index (names + one-liners + param summary) — level 1. Reference it; don't inline the whole thing into a prompt. | `... python -m composer.cli catalog` |
65
78
  | `describe` | Full ports/params/example/cost for ONE node — level 2, on-demand deep-dive. | `... python -m composer.cli describe candles_fetch` |
66
79
 
@@ -76,17 +89,17 @@ interprets it) — plus any `requires:` capabilities. Full field definitions liv
76
89
  `composer/authoring/answers.py`; the shape:
77
90
 
78
91
  ```yaml
79
- archetype: trend_momentum # trend_momentum | thesis_fund | classifier
92
+ archetype: trend_momentum # trend_momentum | breakout | thesis_fund | classifier
80
93
  strategy: my_strat # short id -> <id>.graph.yaml
81
94
  universe:
82
- whitelist: ["BTC", "ETH", "SOL"] # fixed asset list (trend_momentum)
95
+ whitelist: ["BTC", "ETH", "SOL"] # fixed asset list (trend_momentum / breakout)
83
96
  long_basket: [] # thesis_fund LONG leg (use instead of whitelist)
84
97
  short_basket: [] # thesis_fund SHORT leg
85
- discover: false # classifier: derive the universe from the live board
86
- dex: "" # classifier discovery: venue filter
87
- name_prefix: "" # classifier discovery: instrument name prefix
88
- max_leverage_cap: 5.0 # classifier discovery: leverage ceiling
89
- min_day_volume: 0.0 # classifier discovery: daily-volume floor
98
+ discover: false # classifier / breakout: derive the universe from the live board
99
+ dex: "" # discovery: venue filter
100
+ name_prefix: "" # discovery: instrument name prefix
101
+ max_leverage_cap: 5.0 # discovery: leverage ceiling
102
+ min_day_volume: 0.0 # discovery: daily-volume floor
90
103
  intervals: ["4h"] # candle intervals, e.g. ["1h", "4h"]
91
104
  max_positions: 1 # 1 = single best; >1 = rank a pool, cap to open slots
92
105
  ttl_seconds: 240 # signal-dedup memory window
@@ -106,17 +119,62 @@ edge: >
106
119
  This is carried verbatim into the graph; nothing parses it, so be as specific as you want.
107
120
  requires: [] # [{name, kind, need}] — a capability that may not exist yet;
108
121
  # a name the registry lacks becomes a gap report, nothing written
122
+ # --- OPTIONAL expressiveness (omit both for the archetype's working defaults) ---
123
+ indicators: # ADD registry indicator nodes and wire them into the scorer,
124
+ # so they become addressable by id from `scoring` below.
125
+ - id: fresh_raw # the node's graph id (a feature key = SOURCE NODE ID)
126
+ node: recent_high_bars_ago # any registry indicator (see `composer catalog`)
127
+ with: { window: 24, interval: "1h" } # the node's params
128
+ # port: bars_ago # optional: which output feeds the scorer (default: the node's sole output)
129
+ - id: fresh # a THRESHOLDED gate over the raw feature above (C1b)
130
+ node: threshold_gate # compare a per-asset feature vs a threshold -> 1.0/0.0
131
+ with: { op: lte, value: 5 } # op is lt|lte|gt|gte; here: bars_ago <= 5 means "fresh"
132
+ source: fresh_raw # CHAIN the input off fresh_raw's output (not candles)
133
+ scoring: # RE-SELECT the scorer's hard-vs-weighted split over the feature
134
+ # nodes (built-in + any `indicators` above). Each field omitted
135
+ # -> keep the archetype default; set -> override it.
136
+ required_gates: ["brk", "fresh", "vol", "lean"] # feature ids that must ALL pass (hard gates)
137
+ weights: { mom: 2.0, vol: 1.0, lean: 0.05 } # feature id -> weighted-sum weight
138
+ min_score: 0 # minimum score to emit
109
139
  ```
110
140
 
111
141
  **Universe modes**: `whitelist` is a fixed list you already know (trend_momentum,
112
- trend_conviction shapes). `long_basket`/`short_basket` are the two legs of a fixed macro
113
- basket (thesis_fund — a relative-value long/short). `discover: true` (+ `dex`,
142
+ breakout, trend_conviction shapes). `long_basket`/`short_basket` are the two legs of a
143
+ fixed macro basket (thesis_fund — a relative-value long/short). `discover: true` (+ `dex`,
114
144
  `name_prefix`, `max_leverage_cap`, `min_day_volume`) derives the tradeable set each tick
115
- from the live instrument board by a signature instead of a fixed list (classifier).
116
-
117
- An `archetype` the composer doesn't recognize (i.e. not `trend_momentum` /
118
- `thesis_fund` / `classifier`) falls back to the **off-map skeleton** — still a valid,
119
- generated starting graph, just minimal; expect to do more hand-editing from there.
145
+ from the live instrument board by a signature instead of a fixed list (classifier and
146
+ breakout). `trend_momentum` is fixed-universe only — a `discover: true` answer is rejected
147
+ with a teaching error pointing at `breakout` / `classifier` (it never silently emits an
148
+ empty universe).
149
+
150
+ **Archetypes**: `trend_momentum` (fixed universe → indicators → hard-gates + weighted-sum
151
+ scorer), `breakout` (a directional range-break as the hard gate, fixed basket OR discovered
152
+ universe — the hawk shape), `classifier` (a pure_fn direction classifier on a discovered or
153
+ fixed universe), `thesis_fund` (fixed long/short basket, direction fixed per leg). An
154
+ `archetype` the composer doesn't recognize falls back to the **off-map skeleton** — still a
155
+ valid, generated starting graph, just minimal; expect to do more hand-editing from there.
156
+
157
+ **Expressiveness (`scoring` / `indicators`)**: both are OPTIONAL and default to the
158
+ archetype's built-in scorer. `scoring` re-selects which feature nodes are HARD gates
159
+ (`required_gates`) versus weighted contributors (`weights`) and the score floor
160
+ (`min_score`), addressing feature nodes by their SOURCE NODE ID. `indicators` adds
161
+ registry indicator nodes (e.g. `breakout_detect` with a `window`) and wires them into the
162
+ scorer so they become addressable from `scoring`. A `scoring` name that matches no feature
163
+ node, or an `indicators` node the registry lacks, is a loud generation error — never a
164
+ silent drop. This is how, e.g., the `breakout` archetype expresses a four-hard-gate
165
+ discovery variant without any hand editing.
166
+
167
+ **Thresholded gates (`threshold_gate` + `source`)**: a hard gate must be TRUTHY, so a raw
168
+ numeric feature (e.g. `recent_high_bars_ago`, whose output is bars-since-the-high) can't be
169
+ gated directly — 0 (a high set this bar) reads falsy, a stale 40 reads truthy, exactly
170
+ inverted. Add a `threshold_gate` indicator (`op` one of `lt|lte|gt|gte`, `value` the
171
+ threshold) and CHAIN it off the raw feature with `source:` — its input then reads that node's
172
+ output instead of candles, emitting a real `1.0`/`0.0` gate. So a "fresh entry within 5 bars"
173
+ gate is `fresh = threshold_gate(op: lte, value: 5, source: fresh_raw)` over `fresh_raw =
174
+ recent_high_bars_ago(...)`; a "don't chase a runaway" filter is `threshold_gate(op: lt, value:
175
+ 15, source: own_ret)` over an `own_return_24h` feature. `source` names an EARLIER `indicators`
176
+ entry or a built-in feature node of the archetype; an unknown source, or one whose output
177
+ type doesn't match the consumer's input, is a loud generation error.
120
178
 
121
179
  Get a blank skeleton for any archetype with `composer describe`-adjacent tooling, or just
122
180
  copy the block above and strip fields you don't need — every field has a sane default via
@@ -133,9 +191,29 @@ edit rights over the whole graph: add/remove nodes, rewire edges, retune any par
133
191
  Two ways to express bespoke logic:
134
192
 
135
193
  - **Registry nodes** (`node: <name>`, see `catalog`) — parameterized, pre-built pipeline
136
- steps: sources, indicators, filters, scorers, rankers, the emitter, and the
137
- `ttl_dedup_state` stateful node. Wire per-asset features into a scorer's `features`
138
- input to MERGE them (keyed by producing node id).
194
+ steps: sources, indicators, filters, scorers, rankers, the emitter, and three stateful
195
+ nodes (`ttl_dedup_state` for signal dedup; `snapshot_diff_state` for a bounded rolling
196
+ per-key growth ledger; `cohort_cache_state` to pin an expensive fetch across ticks). Wire
197
+ per-asset features into a scorer's `features` input to MERGE them (keyed by producing node
198
+ id). The cohort/cross-trader vocabulary (the whalehunter thesis) is now expressible end to
199
+ end: `top_traders_fetch` → `cohort_cache_state` → `trader_states_fetch` → `aggregate_bias`
200
+ (the one sanctioned fn→primitive promotion: cross-trader net/gross bias per coin) +
201
+ `snapshot_diff_state` → a bespoke cohort-scoring `pure_fn` → emit. The **world-coverage sources** span the
202
+ whole scanner-usable Senpi MCP read surface (proven fully-mapped by the closure check, see
203
+ below): market signals (funding regime/history, cross-asset flows, prices, momentum events),
204
+ trader intelligence (top-traders + batch trader-state, the fast 4h smart-money leaderboard
205
+ `leaderboard_top_fetch` and its single-trader companion `leaderboard_trader_fetch`, leaderboard
206
+ trader positions, copy-strategy ranking `top_strategies_fetch`, the Arena leaderboard), and
207
+ own-account reads (order/PnL/clearinghouse/history, banked realized-PnL on open positions).
208
+ They emit `record_list` outputs — ordered lists of typed dict records — and, for
209
+ `funding_regime_fetch`, a `label` output (a market-wide categorical string like `LONG_CROWDED`).
210
+ Neither type is asset-keyed: their intended consumer is a `kind: pure_fn` node that declares a
211
+ `record_list`/`label` input and reads the fields itself (e.g. project addresses out of
212
+ `top_traders_fetch.traders` and wire that record_list straight into `trader_states_fetch.traders`
213
+ to pull the cohort's live books; or project `{wallet_address, coin}` pairs into
214
+ `open_positions_realized_pnl_fetch.positions`). Prefer `leaderboard_top_fetch` for CURRENT-
215
+ momentum cohorts (transient 4h delta-PnL) and `top_traders_fetch` for CONSISTENT-performer
216
+ cohorts (multi-window track record) — the two are deliberately different reads.
139
217
  - **`kind: pure_fn` nodes** — how you write bespoke scoring/classification math the
140
218
  registry vocabulary can't express (e.g. the cross-feature conviction waterfall in
141
219
  `composer/examples/conviction_scorer.graph.yaml`, corpus_origin: bison). Rules, statically
@@ -150,6 +228,20 @@ Two ways to express bespoke logic:
150
228
  - it must `return` exactly its declared outputs;
151
229
  - include `tests:` (inline fixture cases: `inputs:` → `expect:`) — these run as part of
152
230
  `composer check`'s `pure_fn_tests` stage.
231
+ - **The escape hatch (`mcp_read` + the `raw` port)** — the LAST RESORT for a read the curated
232
+ source vocabulary doesn't wrap (or doesn't expose the fields you need). `mcp_read` calls a
233
+ named MCP tool with `args` passed verbatim and emits the unwrapped payload as the `raw` type;
234
+ you MUST wire that payload into a `kind: pure_fn` node — the NORMALIZER — which reads it and
235
+ returns TYPED outputs (with inline tests). What it's for: closing a genuine gap without
236
+ waiting for a registry add. What it is NOT: a way to bypass typing (a `raw` wire may feed
237
+ nothing but a pure_fn — CMP039), a general-purpose tool caller (READ-ONLY: the `tool` must be
238
+ in the committed snapshot and must not be a mutation — CMP060/CMP061), or a substitute for a
239
+ first-class node. **Reach for the registry/answers path FIRST** (`composer catalog` /
240
+ `composer describe`); use the hatch only when nothing fits, and be explicit in the `edge:`
241
+ about WHAT you're fetching and WHY the curated path can't express it. Never invent a tool —
242
+ if the read doesn't exist, it can't be faked here. `composer check` ANNOTATES every hatch use
243
+ (and flags when the tool already has a first-class node) so the usage stays visible; recurring
244
+ hatch use of the same shape is the maintainer's signal to wrap it as a real source node.
153
245
 
154
246
  **Teaching errors are actionable, not decorative.** Every error from `validate`/`check`
155
247
  carries a stable code (`CMPxxx`), a location (node id / port / line), what's wrong in one
@@ -210,6 +302,20 @@ what you'd wire in. Re-run `check` after each edit until GREEN.
210
302
  nearest registry alternatives) and writes nothing. That's the correct outcome for
211
303
  something not yet buildable; the fix is adding the capability to the registry, not
212
304
  working around it in the graph.
305
+ - **Stateful nodes carry PRIOR-tick data, and the cohort cache does NOT skip the fetch.**
306
+ Every stateful node is split READ (prologue) / WRITE (epilogue): its outputs are read
307
+ BEFORE the pipeline runs and reflect the LAST persisted tick, while its late input is
308
+ recorded AFTER. Two consequences to author around: (1) `snapshot_diff_state.growth` is the
309
+ delta between the latest STORED snapshot and the oldest in the window — one tick behind this
310
+ tick's fresh input, and empty until ≥2 buckets have accumulated (a short warmup, exactly like
311
+ whalehunter's `requireGrowing` blocking until ~day 2). (2) `cohort_cache_state` does NOT
312
+ rate-limit — sources fire every tick and the compiler emits the upstream fetch
313
+ unconditionally (there is no conditional-fetch machinery). What it buys is COHORT STABILITY:
314
+ the read half serves the last-known-good cohort (pinned for `ttl_hours`, smoothing a transient
315
+ fetch failure, surviving process restarts via `ctx.state`), and the write half refreshes only
316
+ when the TTL lapses, the `version` changes, or the cache was empty. Cold start serves an empty
317
+ cohort for one warmup tick. State is BOUNDED by design (dedup prune, `max_snapshots` cap, one
318
+ cached cohort) — an unbounded fleet-box state series is a real ops hazard.
213
319
  - **Ambiguous ideas need answers first, not invented specifics.** If a brief is missing
214
320
  information you can't responsibly guess (which assets, long/short/both, how much risk),
215
321
  ask the user rather than pick something plausible-sounding. Pin down the answers file,
@@ -226,8 +332,104 @@ what you'd wire in. Re-run `check` after each edit until GREEN.
226
332
  a GREEN `check` (which now includes the `runtime_validate` stage — the real Zod + DSL oracle)
227
333
  and stages a standalone strategy PACKAGE TREE, idempotent and side-effect-free:
228
334
  `strategy.yaml` (catalog + funding manifest), `main/runtime.yaml` (the complete runtime:
229
- strategy/scanners/actions/**exit**/risk), `main/scanners/scan.py`, `main/<id>.graph.yaml`, and
230
- `manifest.json` (hashing all three, with the runtime's Zod verdict). There is no host
231
- `runtime.yaml` to borrow — the staged unit is complete on its own. Install is `senpi-strategy-ops`:
335
+ strategy/scanners/actions/**exit**/risk), `main/scanners/scan.py`,
336
+ `main/scanners/strategy_primitives/` (a vendored SNAPSHOT of `composer/primitives/`, a sibling
337
+ package of `scan.py`), `main/<id>.graph.yaml`, and `manifest.json` (hashing every artifact
338
+ including each vendored primitives file, with the runtime's Zod verdict). The emitted `scan.py`
339
+ imports `strategy_primitives`, NEVER `composer.*`, so the unit is self-contained: it runs on a
340
+ box that has none of the composer checkout — the scaffold puts the scanner dir on `sys.path` and
341
+ the vendored package resolves as a sibling of `scan.py`. The snapshot freezes the primitives at
342
+ deploy time (later `composer/primitives/` changes never mutate an already-staged strategy). There
343
+ is no host `runtime.yaml` to borrow — the staged unit is complete on its own. Install is `senpi-strategy-ops`:
232
344
  it reads `strategy.yaml.instances[]`, mints one funded wallet per instance, and renders each
233
345
  `runtime.yaml` onto its wallet. The composer never creates wallets, moves money, or installs.
346
+ - **`install` puts ONE instance of a staged unit onto a box — without touching the frozen runtime.**
347
+ `composer install <staged-dir> --wallet 0x…` (or a graph file plus `-o <staging-dir>`) is the box-side
348
+ step deploy stops short of. It (a) PREFLIGHTS — resolves the staged tree, verifies every artifact's
349
+ bytes against `manifest.json` (CMP201, tamper/hand-edit detection), and requires the recorded oracle
350
+ verdict to be GREEN (`VALID`; a `SKIPPED` verdict on an oracle-less box is allowed, `INVALID` is CMP202);
351
+ (b) COPIES the unit to a CONTENT-ADDRESSED, IMMUTABLE dir `<senpi-state>/strategies/<strategy>/<hash8>/`
352
+ — written to a temp sibling, every file re-verified against the manifest (CMP203), then atomic-renamed
353
+ into place, and NEVER edited after (so the frozen inline-YAML in the registry can't drift from the
354
+ scan.py re-read off disk on a scaffold respawn). `hash8` = the first 8 hex of a sha256 over the manifest
355
+ `artifacts` map, so identical staged content always maps to the same dir and a re-install is an idempotent
356
+ no-op; (c) BINDS the real `--wallet` for the `${..._WALLET}` placeholder in the COPIED `runtime.yaml` only
357
+ (the staged source stays pristine), records the binding in `install.json`, and re-runs the placeholder
358
+ policy — any remaining bare `${VAR}` after binding is CMP115; (d) INVOKES the FROZEN consumer,
359
+ `openclaw senpi runtime create -p <hashdir>/main/runtime.yaml` (PATH mode — never `--content`), behind an
360
+ injectable seam; (e) maps the known installRuntime failure classes (duplicate id CMP210,
361
+ one-runtime-per-wallet CMP211, wallet not ACTIVE CMP212, invalid YAML CMP213, entrypoint missing CMP214)
362
+ to teaching errors that name the exact next command. The runtime id is `<strategy>-<hash8>` (content-keyed,
363
+ so a duplicate is meaningful). Every step's completion is derivable from disk (staged tree / hash dir +
364
+ `install.json` / the box registry entry) — the verb is resumable-by-artifact, with no hidden session state.
365
+ The wallet is supplied by `--wallet`, or auto-bound from the `composer fund` `wallet.json` when `--wallet`
366
+ is omitted (see `fund`, below); the update/reinstall (delete+create) flow is OUT of scope (the sanctioned
367
+ teardown path is `composer close`, below).
368
+ - **`fund` creates + funds the wallet — inside the flow, with an EXPLICIT budget.** `composer fund
369
+ <staged-dir> --budget <usd>` is the funding step the composer used to hand off conversationally. `--budget`
370
+ is REQUIRED and EXPLICIT (≥ $100): agree it with the user and pass the exact amount. It is NEVER derived
371
+ from a `funding_share` nor split across wallets — that legacy ops derivation misfired on a real box
372
+ (it bridged in pieces and told a user with sufficient balance to top up), so this verb funds ONE wallet
373
+ with the ONE number you pass. Steps, resumable-by-artifact: (a) if `<senpi-state>/strategies/<strategy>/wallet.json`
374
+ exists (STRATEGY-level, ABOVE the `<hash8>` dirs — the wallet is the strategy's instance identity across
375
+ content versions), verify it via MCP `strategy_list` — ACTIVE → `FUNDED` no-op, still initializing →
376
+ `PENDING`, closed/gone → CMP231 (stale); (b) otherwise PREFLIGHT the visible balance via
377
+ `account_get_portfolio` (free Hyperliquid + free EVM USDC, summed as the backend funds a create) — short
378
+ of budget → CMP232 stating the current visible total, the exact shortfall, the bridged-funds VISIBILITY
379
+ LAG (funds moved HL→EVM can lag ~a minute; wait + re-run), and the top-up options (the composer NEVER
380
+ bridges); (c) sufficient → call MCP `strategy_create_custom_strategy` (`skillName` = the strategy name,
381
+ matching what `composer close`'s legacy discovery reads), record `wallet.json` BEFORE polling, then poll
382
+ briefly to ACTIVE. `strategy_create_custom_strategy` is a MONEY MUTATION: there is no `--yes` (the CLI is
383
+ agent-driven — the user's confirmation is conversational per the skill), but the verb prints an
384
+ unmistakable pre-action line (creating wallet, budget, funding source) in its result. Missing MCP creds
385
+ is a loud CMP223. Then `composer install` (omit `--wallet`) auto-binds the funded wallet.
386
+ - **`close` is the ONLY sanctioned teardown — never a raw `strategy_close`.** The invariant is
387
+ non-negotiable: STOP the runtime first (`openclaw senpi runtime delete <id>`), CONFIRM it is gone,
388
+ and ONLY THEN submit MCP `strategy_close(<strategyId>)` (which flattens ALL positions and returns
389
+ funds). A raw `strategy_close` with a live runtime STRANDS the runtime — it keeps trading a wallet the
390
+ server is tearing down. `composer close <target>` enforces this behind injectable seams: if the runtime
391
+ delete fails (CMP220) or the runtime is still present afterward (CMP221), `strategy_close` is WITHHELD
392
+ and the verb fails loud rather than strand or double-fire. It is IDEMPOTENT and SUBMIT-ONLY (no poll):
393
+ `strategy_close` is async, so the verb reports `CLOSING` and re-running it IS the poll — a re-run skips
394
+ the delete (runtime already gone) and skips the re-submit (strategy left the ACTIVE set), reporting
395
+ `CLOSED` once the strategy is terminal. It resolves the target from the composer's own on-box artifacts
396
+ first (a canonical hash dir's `install.json`, or a strategy name / runtime id under `--state-dir`), and
397
+ falls back to `strategy_list` attribution (skillName / strategyId + the wallet read from the strategy
398
+ record) so it also closes legacy ops-deployed strategies and orphan runtimes. Missing MCP creds
399
+ (`SENPI_API_KEY`) is a loud CMP223, not a silent skip.
400
+ - **`status --state-dir` is the resumable-by-artifact install chain.** With a box named, `status` walks
401
+ the current staged content DOWN onto the box entirely from artifacts + one read-only `runtime list`:
402
+ landed (canonical hash dir + `install.json`) → registered (`installed_runtimes.json`) → running. Multiple
403
+ hash dirs for one strategy are distinguished — which hash is installed/running vs a merely-landed prior
404
+ version. Liveness is reported HONESTLY: `runtime list` proves only that the process is up, so a running
405
+ install is annotated that functional liveness (scan() `runCount>0`, a fresh `lastRunFinishedAt`,
406
+ `consecutiveErrorCount==0` — see `senpi-strategy-ops/references/liveness-verification.md`) is NOT checked
407
+ here; run `openclaw senpi state -r <id> --json` for that. "running" ≠ "operating".
408
+
409
+ ## 8. Debugging generation deterministically
410
+
411
+ Generation (`new`/`check`/`deploy`) is fully offline-reproducible: no creds, no network, no
412
+ wall-clock — the same answers always produce the same bytes. So any "the composer generated the
413
+ wrong thing" report replays exactly. The procedure (proven on the first real dev-test incident):
414
+
415
+ 1. Reconstruct the user's answers file from the conversation (the graph's `metadata` block IS the
416
+ answers when you have the generated graph — spec-is-state).
417
+ 2. Drive the CLI into a scratch dir: `python -m composer.cli new --archetype <a> --answers <f> -o /tmp/repro`
418
+ (deps: pyyaml, pydantic, jinja2, jsonschema — a throwaway venv or `uvx` is enough).
419
+ 3. Diff the emitted graph against what the user agreed to — the delta names the lost intent.
420
+ 4. Read the archetype's `build()` in `composer/authoring/archetypes/` for where that intent was
421
+ dropped (a hardcoded literal, an unread answer field, a missing archetype route).
422
+
423
+ Classify before fixing: COVERAGE (no archetype+params expresses it) · EXPRESSIVENESS (the answers
424
+ surface can't say it) · ROUTING (wrong archetype chosen) · INSTRUCTION (the skill never told the
425
+ agent to pass it). Each has a different smallest fix — and only genuinely-missing SOURCE
426
+ capability (no data feed in the world) is a registry gap; everything else is composer-side.
427
+
428
+ **Maintainer note (source coverage).** The source vocabulary is kept EXHAUSTIVE over the scanner-
429
+ usable Senpi MCP read surface, and that claim is enforced continuously by the CI closure check
430
+ (`composer/registry/mcp_closure.py`, offline by default). If a new server-side read tool appears,
431
+ CI goes red until it is either mapped by a new source node (declaring `mcp_tool="..."`) or added to
432
+ the allowlist with a reason. To pick up a catalog change, run
433
+ `MCP_SNAPSHOT_REFRESH=1 pytest python-tests/composer/test_mcp_closure.py` (fetches the live
434
+ unauthenticated `tools/list`, rewrites `mcp-tools-snapshot.json`, and surfaces input-schema drift),
435
+ then commit the refreshed snapshot. Full design in `docs/composer/design-models.md` §9.