ace-sim 0.13.0
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.
- checksums.yaml +7 -0
- data/.ace-defaults/nav/protocols/wfi-sources/ace-sim.yml +19 -0
- data/.ace-defaults/sim/config.yml +9 -0
- data/.ace-defaults/sim/presets/validate-idea.yml +10 -0
- data/.ace-defaults/sim/presets/validate-task.yml +9 -0
- data/.ace-defaults/sim/steps/draft.md +41 -0
- data/.ace-defaults/sim/steps/plan.md +54 -0
- data/.ace-defaults/sim/steps/work.md +54 -0
- data/CHANGELOG.md +266 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +12 -0
- data/docs/demo/ace-sim-run-4x.gif +0 -0
- data/docs/demo/ace-sim-run.gif +0 -0
- data/docs/demo/ace-sim-run.tape.yml +24 -0
- data/docs/getting-started.md +95 -0
- data/docs/handbook.md +24 -0
- data/docs/usage.md +127 -0
- data/exe/ace-sim +15 -0
- data/handbook/skills/as-sim-run/SKILL.md +29 -0
- data/handbook/workflow-instructions/sim/run.wf.md +155 -0
- data/lib/ace/sim/cli/commands/run.rb +139 -0
- data/lib/ace/sim/cli.rb +48 -0
- data/lib/ace/sim/models/simulation_session.rb +85 -0
- data/lib/ace/sim/molecules/final_synthesis_executor.rb +231 -0
- data/lib/ace/sim/molecules/session_store.rb +66 -0
- data/lib/ace/sim/molecules/source_bundler.rb +70 -0
- data/lib/ace/sim/molecules/stage_executor.rb +106 -0
- data/lib/ace/sim/molecules/synthesis_builder.rb +41 -0
- data/lib/ace/sim/organisms/simulation_runner.rb +172 -0
- data/lib/ace/sim/version.rb +7 -0
- data/lib/ace/sim.rb +132 -0
- metadata +177 -0
data/docs/handbook.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: user
|
|
3
|
+
title: ace-sim Handbook
|
|
4
|
+
purpose: Package-specific docs and workflow catalog
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-03-22
|
|
7
|
+
last-checked: 2026-03-22
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# ace-sim Handbook
|
|
11
|
+
|
|
12
|
+
## Skills
|
|
13
|
+
|
|
14
|
+
- [`as-sim-run`](../handbook/skills/as-sim-run/SKILL.md)
|
|
15
|
+
|
|
16
|
+
## Workflows
|
|
17
|
+
|
|
18
|
+
- [`sim/run`](../handbook/workflow-instructions/sim/run.wf.md)
|
|
19
|
+
|
|
20
|
+
## Related Docs
|
|
21
|
+
|
|
22
|
+
- [Getting Started](getting-started.md)
|
|
23
|
+
- [Usage Reference](usage.md)
|
|
24
|
+
- Command help: `ace-sim --help`
|
data/docs/usage.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: reference
|
|
3
|
+
title: ace-sim Usage Reference
|
|
4
|
+
purpose: Complete CLI reference for simulation runs and command behavior
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-03-22
|
|
7
|
+
last-checked: 2026-03-22
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# ace-sim Usage Reference
|
|
11
|
+
|
|
12
|
+
`ace-sim` runs provider simulations through configurable steps and presets.
|
|
13
|
+
|
|
14
|
+
## Command Overview
|
|
15
|
+
|
|
16
|
+
- `ace-sim` — entrypoint
|
|
17
|
+
- `ace-sim run` — run a simulation preset with one or more source files
|
|
18
|
+
- `ace-sim --help` — print command list and examples
|
|
19
|
+
|
|
20
|
+
## `ace-sim --help`
|
|
21
|
+
|
|
22
|
+
Shows command examples and the `run` subcommand.
|
|
23
|
+
|
|
24
|
+
## `ace-sim run`
|
|
25
|
+
|
|
26
|
+
### Syntax
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ace-sim run [OPTIONS]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Purpose
|
|
33
|
+
|
|
34
|
+
Execute a preset-driven simulation with source files, provider list, and optional final synthesis.
|
|
35
|
+
|
|
36
|
+
### Options
|
|
37
|
+
|
|
38
|
+
| Option | Type | Default / Source | Description |
|
|
39
|
+
|---|---|---|---|
|
|
40
|
+
| `--preset` | string | config `sim.default_preset` or `validate-idea` | Preset name from `.ace/sim/presets/*.yml|yaml` |
|
|
41
|
+
| `--source` | array | preset `source` | One or more source files (repeatable, supports globs) |
|
|
42
|
+
| `--steps` | string | preset steps | Comma-separated step names (override preset step list) |
|
|
43
|
+
| `--provider` | array | preset providers | Provider:model values (`--provider` may repeat) |
|
|
44
|
+
| `--repeat` | integer | sim default repeat (or `1`) | Run each provider this many times |
|
|
45
|
+
| `--synthesis-workflow` | string | preset / config | Workflow/file ref for final synthesis |
|
|
46
|
+
| `--synthesis-provider` | string | preset / config provider | Provider for final suggestions generation |
|
|
47
|
+
| `--dry-run` | flag | preset / false | Prepare and preview without mutating providers |
|
|
48
|
+
| `--writeback` | flag | preset / false | Write final revised source back to source when set |
|
|
49
|
+
| `--quiet`, `-q` | flag | false | Suppress non-essential status output |
|
|
50
|
+
| `--verbose`, `-v` | flag | false | Print extended diagnostics |
|
|
51
|
+
| `--help`, `-h` | flag | false | Show command help |
|
|
52
|
+
|
|
53
|
+
## Preset configuration model
|
|
54
|
+
|
|
55
|
+
- Presets are resolved by name.
|
|
56
|
+
- Preset loading precedence for files is:
|
|
57
|
+
- gem defaults (`.ace-defaults/sim/presets`)
|
|
58
|
+
- user presets (`~/.ace/sim/presets`)
|
|
59
|
+
- project presets (`.ace/sim/presets`)
|
|
60
|
+
|
|
61
|
+
- File extensions: `.yml` and `.yaml`.
|
|
62
|
+
|
|
63
|
+
If a preset is missing but known, fallback behavior is an empty preset with default steps and the system-level defaults for provider/repeat behavior.
|
|
64
|
+
|
|
65
|
+
## Step config resolution
|
|
66
|
+
|
|
67
|
+
Each requested step is resolved in this order:
|
|
68
|
+
|
|
69
|
+
1. `.ace/sim/steps/<step>.md`
|
|
70
|
+
2. `~/.ace/sim/steps/<step>.md`
|
|
71
|
+
3. `.ace-defaults/sim/steps/<step>.md`
|
|
72
|
+
|
|
73
|
+
Run fails with `Missing step config` if a required step file is not found.
|
|
74
|
+
|
|
75
|
+
## Synthesis and precedence
|
|
76
|
+
|
|
77
|
+
- If `--synthesis-provider` is passed, that provider is used for final synthesis.
|
|
78
|
+
- If not passed, synthesis defaults use: preset `synthesis_provider`, then global config `sim.synthesis_provider`.
|
|
79
|
+
- `--synthesis-provider` requires `--synthesis-workflow` to be set.
|
|
80
|
+
- `--dry-run` is a non-mutating preview and cannot be combined with `--writeback`.
|
|
81
|
+
|
|
82
|
+
## Artifacts
|
|
83
|
+
|
|
84
|
+
Run output lives under `.ace-local/sim/simulations/<run-id>/`.
|
|
85
|
+
|
|
86
|
+
Run root:
|
|
87
|
+
|
|
88
|
+
- `session.yml` — simulation session metadata
|
|
89
|
+
- `synthesis.yml` — final synthesis status and summaries
|
|
90
|
+
- `input.md` — bundled source content used for provider execution
|
|
91
|
+
- `input.bundle.md` — source bundle manifest generated before execution
|
|
92
|
+
|
|
93
|
+
Per chain (`<provider>-<iteration>`):
|
|
94
|
+
|
|
95
|
+
- `NN-step/input.md` — effective input for that step
|
|
96
|
+
- `NN-step/user.bundle.md` — step bundle for LLM prompt
|
|
97
|
+
- `NN-step/user.prompt.md` — resolved prompt file
|
|
98
|
+
- `NN-step/output.md` — provider output for that step
|
|
99
|
+
|
|
100
|
+
Final directory:
|
|
101
|
+
|
|
102
|
+
- `final/input.md` — combined chain outputs
|
|
103
|
+
- `final/user.bundle.md` — final synthesis bundle
|
|
104
|
+
- `final/user.prompt.md` — final synthesis prompt
|
|
105
|
+
- `final/output.sequence.md` — raw LLM output sequence
|
|
106
|
+
- `final/suggestions.report.md` — parsed suggestions block
|
|
107
|
+
- `final/source.original.md` — original source snapshot
|
|
108
|
+
- `final/source.revised.md` — revised source output (if synthesis enabled)
|
|
109
|
+
|
|
110
|
+
## Behavior notes
|
|
111
|
+
|
|
112
|
+
- Steps run sequentially within each chain — each step's output becomes the next step's input, so later steps build on earlier reasoning.
|
|
113
|
+
- `draft`, `plan`, `work` are common defaults; custom step order is supported via `--steps`.
|
|
114
|
+
- After all chains complete, the synthesis stage gathers feedback from every stage to propose improvements and produce a revised source artifact.
|
|
115
|
+
- Synthesis is optional; enable via preset or explicit `--synthesis-workflow`.
|
|
116
|
+
- `--dry-run` does not perform provider calls.
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
119
|
+
|
|
120
|
+
- `Unknown preset`:
|
|
121
|
+
- verify preset exists under one of `.ace-defaults/sim/presets`, `~/.ace/sim/presets`, or `.ace/sim/presets`
|
|
122
|
+
- `Missing step config`:
|
|
123
|
+
- verify step bundles exist in step search path above
|
|
124
|
+
- `--source is required`:
|
|
125
|
+
- provide source files directly or via preset defaults
|
|
126
|
+
- `synthesis_provider requires synthesis_workflow`:
|
|
127
|
+
- include both flags together when overriding synthesis provider
|
data/exe/ace-sim
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "ace/sim"
|
|
6
|
+
|
|
7
|
+
args = ARGV.empty? ? ["--help"] : ARGV
|
|
8
|
+
|
|
9
|
+
begin
|
|
10
|
+
exit_code = Ace::Sim::CLI.start(args)
|
|
11
|
+
exit(exit_code) if exit_code.is_a?(Integer) && exit_code.nonzero?
|
|
12
|
+
rescue Ace::Support::Cli::Error => e
|
|
13
|
+
warn e.message
|
|
14
|
+
exit(e.exit_code)
|
|
15
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: as-sim-run
|
|
3
|
+
description: Run scenario simulation with provider comparison
|
|
4
|
+
# bundle: wfi://sim/run
|
|
5
|
+
# agent: general-purpose
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools:
|
|
8
|
+
- Bash(ace-sim:*)
|
|
9
|
+
- Bash(ace-bundle:*)
|
|
10
|
+
- Read
|
|
11
|
+
- TodoWrite
|
|
12
|
+
argument-hint: "[--preset NAME] [--source PATH]"
|
|
13
|
+
last_modified: 2026-02-28
|
|
14
|
+
source: ace-sim
|
|
15
|
+
integration:
|
|
16
|
+
targets:
|
|
17
|
+
- claude
|
|
18
|
+
- codex
|
|
19
|
+
- gemini
|
|
20
|
+
- opencode
|
|
21
|
+
- pi
|
|
22
|
+
providers: {}
|
|
23
|
+
skill:
|
|
24
|
+
kind: workflow
|
|
25
|
+
execution:
|
|
26
|
+
workflow: wfi://sim/run
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Load and run `ace-bundle wfi://sim/run` in the current project, then follow the loaded workflow as the source of truth and execute it end-to-end instead of only summarizing it.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: workflow
|
|
3
|
+
title: Simulation Run Workflow
|
|
4
|
+
purpose: Run ace-sim simulation with preset, source, and provider configuration
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-02-28
|
|
7
|
+
last-checked: 2026-03-21
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Simulation Run Workflow
|
|
11
|
+
|
|
12
|
+
## Goal
|
|
13
|
+
|
|
14
|
+
Run a simulation against a source document using a preset, then review the synthesis results (suggestions report and revised source).
|
|
15
|
+
|
|
16
|
+
## Arguments
|
|
17
|
+
|
|
18
|
+
- `--preset`: Preset name (optional). Available presets:
|
|
19
|
+
- `validate-task` — steps: plan, work (synthesis: `wfi://task/review`)
|
|
20
|
+
- `validate-idea` — steps: draft, plan, work (synthesis: `wfi://idea/review`)
|
|
21
|
+
- `--source`: Source markdown file path (required unless preset defines a default source)
|
|
22
|
+
- `--provider`: Provider:model override (repeatable). Examples: `google:flash-preview`, `google:pro-preview`
|
|
23
|
+
- `--synthesis-provider`: Provider:model for final synthesis. Example: `claude:haiku`
|
|
24
|
+
- `--dry-run`: Preview what would run without executing
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
### Step 1: Resolve Source
|
|
29
|
+
|
|
30
|
+
If the source is a task number or multiple files, use `ace-bundle` to merge into a single file:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Single task
|
|
34
|
+
ace-bundle task:148 --output /tmp/sim-source.md
|
|
35
|
+
|
|
36
|
+
# Task spec with usage context (recommended for validate-task)
|
|
37
|
+
ace-sim run --preset validate-task --source "path/to/task.s.md,path/to/ux/usage.md"
|
|
38
|
+
|
|
39
|
+
# Already a single file — use directly
|
|
40
|
+
# --source path/to/spec.md
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If `--source` points to an existing file, use it directly.
|
|
44
|
+
|
|
45
|
+
If the source is a task with usage documentation (`ux/usage.md`), include both spec and usage files to give the simulation behavioral acceptance context.
|
|
46
|
+
|
|
47
|
+
### Step 2: Run Simulation
|
|
48
|
+
|
|
49
|
+
Execute `ace-sim run` with the resolved source and any overrides.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# With preset (defaults)
|
|
53
|
+
ace-sim run --preset validate-task --source path/to/source.md
|
|
54
|
+
|
|
55
|
+
# With provider override
|
|
56
|
+
ace-sim run --preset validate-task --source path/to/source.md --provider google:flash-preview
|
|
57
|
+
|
|
58
|
+
# With synthesis provider override
|
|
59
|
+
ace-sim run --preset validate-task --source path/to/source.md --synthesis-provider google:pro-preview
|
|
60
|
+
|
|
61
|
+
# Dry run — preview without executing
|
|
62
|
+
ace-sim run --preset validate-task --source path/to/source.md --dry-run
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Important for Claude Code**: Run with 10-minute timeout (600000ms) and wait for completion inline (not background). Simulations may take several minutes depending on step count and provider.
|
|
66
|
+
|
|
67
|
+
#### Execution Guard (Mandatory)
|
|
68
|
+
|
|
69
|
+
- Completion is defined by **process exit** (success or failure), not by partial output.
|
|
70
|
+
- Do **not** treat temporary silence/no new output as completion.
|
|
71
|
+
- Do **not** run any Step 3+ commands until Step 2 process exit is confirmed.
|
|
72
|
+
- If 10-minute timeout (600000ms) is reached, report timeout and last observed output, then stop dependent steps.
|
|
73
|
+
|
|
74
|
+
Wait for the simulation process to exit. Note the **Run Dir** path from the output.
|
|
75
|
+
|
|
76
|
+
### Step 3: Review Results
|
|
77
|
+
|
|
78
|
+
After successful completion, read the synthesis output files from the run directory:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Read the suggestions report
|
|
82
|
+
Read: <run-dir>/final/suggestions.report.md
|
|
83
|
+
|
|
84
|
+
# Read the revised source document
|
|
85
|
+
Read: <run-dir>/final/source.revised.md
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The run directory structure:
|
|
89
|
+
```
|
|
90
|
+
<run-dir>/
|
|
91
|
+
chains/ # Individual chain execution outputs
|
|
92
|
+
final/
|
|
93
|
+
suggestions.report.md # Synthesis report with findings
|
|
94
|
+
source.revised.md # Revised source incorporating suggestions
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Step 4: Apply Validated Changes
|
|
98
|
+
|
|
99
|
+
After reviewing `source.revised.md`, apply the validated refinements back to the
|
|
100
|
+
original source files. The simulation output is only useful if it feeds back into
|
|
101
|
+
the actual specs.
|
|
102
|
+
|
|
103
|
+
**When the source was a single file** — apply diffs directly using the Edit tool,
|
|
104
|
+
comparing `source.revised.md` against the original.
|
|
105
|
+
|
|
106
|
+
**When the source was bundled from multiple files** (e.g. merged task specs):
|
|
107
|
+
- Compare `source.revised.md` section by section against each original file
|
|
108
|
+
- Apply each relevant change to its correct source file using the Edit tool
|
|
109
|
+
- Common changes to propagate: status promotions, spec refinements, new error
|
|
110
|
+
messages, updated success criteria, clarified edge cases
|
|
111
|
+
|
|
112
|
+
**Skip or flag for human decision:**
|
|
113
|
+
- Changes marked `[PENDING DECISION]` in the revised source
|
|
114
|
+
- Structural additions that don't have a clear home in the originals
|
|
115
|
+
- Questions from the suggestions report — surface these to the user instead
|
|
116
|
+
|
|
117
|
+
Do not commit yet — let the commit workflow handle that after review.
|
|
118
|
+
|
|
119
|
+
### Step 5: Present Summary
|
|
120
|
+
|
|
121
|
+
Summarize the simulation findings to the user:
|
|
122
|
+
|
|
123
|
+
1. **Run metadata** — preset, providers, step count, run directory
|
|
124
|
+
2. **Key findings** — top suggestions from the suggestions report
|
|
125
|
+
3. **Changes applied** — which original files were updated and what changed
|
|
126
|
+
4. **Pending decisions** — any `[PENDING DECISION]` items needing human input
|
|
127
|
+
5. **Recommended actions** — next steps (commit, re-run with different providers, etc.)
|
|
128
|
+
|
|
129
|
+
## Quick Reference
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# List available presets
|
|
133
|
+
ace-sim run --help
|
|
134
|
+
|
|
135
|
+
# Validate a task spec
|
|
136
|
+
ace-sim run --preset validate-task --source path/to/task-spec.md
|
|
137
|
+
|
|
138
|
+
# Validate an idea
|
|
139
|
+
ace-sim run --preset validate-idea --source path/to/idea.md
|
|
140
|
+
|
|
141
|
+
# Override providers
|
|
142
|
+
ace-sim run --preset validate-task --source spec.md --provider google:flash-preview --provider google:pro-preview
|
|
143
|
+
|
|
144
|
+
# Dry run
|
|
145
|
+
ace-sim run --preset validate-task --source spec.md --dry-run
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Success Criteria
|
|
149
|
+
|
|
150
|
+
- [ ] Simulation completed without errors
|
|
151
|
+
- [ ] Synthesis report reviewed (`final/suggestions.report.md`)
|
|
152
|
+
- [ ] Revised source reviewed (`final/source.revised.md`)
|
|
153
|
+
- [ ] Validated changes applied back to original source files
|
|
154
|
+
- [ ] Pending decisions surfaced to user
|
|
155
|
+
- [ ] Key findings summarized to user
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Sim
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
class Run < Ace::Support::Cli::Command
|
|
8
|
+
include Ace::Support::Cli::Base
|
|
9
|
+
|
|
10
|
+
desc "Run preset simulation"
|
|
11
|
+
|
|
12
|
+
option :preset, type: :string, desc: "Preset name (configured in .ace/sim/presets/*.yml)"
|
|
13
|
+
option :source, type: :array, desc: "Source file(s) - repeatable, supports globs"
|
|
14
|
+
option :steps, type: :string, desc: "Comma-separated step names"
|
|
15
|
+
option :provider, type: :array, desc: "Provider:model (repeatable, e.g. --provider codex:mini)"
|
|
16
|
+
option :repeat, type: :integer, desc: "Repeat count for each provider"
|
|
17
|
+
option :synthesis_workflow, type: :string, desc: "Workflow/file reference for final synthesis (e.g. wfi://task/review, wfi://idea/review)"
|
|
18
|
+
option :synthesis_provider, type: :string, desc: "Provider:model for final suggestions synthesis"
|
|
19
|
+
option :dry_run, type: :boolean, desc: "Enable non-mutating run"
|
|
20
|
+
option :writeback, type: :boolean, desc: "Enable writeback"
|
|
21
|
+
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
22
|
+
option :verbose, aliases: ["-v"], type: :boolean, default: false, desc: "Verbose output"
|
|
23
|
+
|
|
24
|
+
def initialize(runner: nil)
|
|
25
|
+
super()
|
|
26
|
+
@runner = runner
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def call(**options)
|
|
30
|
+
session = build_session(options)
|
|
31
|
+
result = runner.run(session)
|
|
32
|
+
|
|
33
|
+
unless options[:quiet]
|
|
34
|
+
puts "Run ID: #{result[:run_id]}"
|
|
35
|
+
puts "Run Dir: #{result[:run_dir]}"
|
|
36
|
+
puts "Status: #{result[:status]}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
return if result[:success]
|
|
40
|
+
|
|
41
|
+
raise Ace::Support::Cli::Error.new(result[:error] || "Simulation failed")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def build_session(options)
|
|
47
|
+
preset_name = pick_value(options[:preset], Ace::Sim.default_preset_name)
|
|
48
|
+
preset_data = Ace::Sim.load_preset(preset_name)
|
|
49
|
+
if preset_data.nil?
|
|
50
|
+
raise Ace::Support::Cli::Error.new("Unknown preset '#{preset_name}'. Known presets: #{Ace::Sim.preset_names.join(", ")}")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if !options[:synthesis_provider].to_s.strip.empty? && options[:synthesis_workflow].nil?
|
|
54
|
+
raise Ace::Support::Cli::Error.new("synthesis_provider requires synthesis_workflow")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
sources = options[:source] || Array(preset_data["source"])
|
|
58
|
+
raise Ace::Support::Cli::Error.new("--source is required") if sources.empty?
|
|
59
|
+
|
|
60
|
+
steps = if options[:steps] && !options[:steps].to_s.strip.empty?
|
|
61
|
+
parse_steps(options[:steps])
|
|
62
|
+
else
|
|
63
|
+
Ace::Sim.normalize_list(preset_data["steps"] || Ace::Sim.get("sim", "default_steps"))
|
|
64
|
+
end
|
|
65
|
+
raise Ace::Support::Cli::Error.new("At least one step is required") if steps.empty?
|
|
66
|
+
|
|
67
|
+
providers = if options[:provider].nil?
|
|
68
|
+
Ace::Sim.normalize_list(preset_data["provider"] || preset_data["providers"] || Ace::Sim.get("sim", "default_providers"))
|
|
69
|
+
else
|
|
70
|
+
Ace::Sim.normalize_list(options[:provider])
|
|
71
|
+
end
|
|
72
|
+
raise Ace::Support::Cli::Error.new("At least one --provider is required") if providers.empty?
|
|
73
|
+
|
|
74
|
+
repeat = pick_value(options[:repeat], preset_data["repeat"], Ace::Sim.get("sim", "default_repeat") || 1)
|
|
75
|
+
repeat = Integer(repeat)
|
|
76
|
+
raise Ace::Support::Cli::Error.new("--repeat must be >= 1") if repeat < 1
|
|
77
|
+
|
|
78
|
+
synthesis_workflow = pick_value(
|
|
79
|
+
options[:synthesis_workflow],
|
|
80
|
+
preset_data["synthesis_workflow"],
|
|
81
|
+
Ace::Sim.get("sim", "synthesis_workflow")
|
|
82
|
+
).to_s.strip
|
|
83
|
+
synthesis_provider = pick_value(
|
|
84
|
+
options[:synthesis_provider],
|
|
85
|
+
preset_data["synthesis_provider"],
|
|
86
|
+
Ace::Sim.get("sim", "synthesis_provider")
|
|
87
|
+
).to_s.strip
|
|
88
|
+
|
|
89
|
+
dry_run = pick_value(options[:dry_run], preset_data["dry_run"], false)
|
|
90
|
+
writeback = pick_value(options[:writeback], preset_data["writeback"], Ace::Sim.get("sim", "writeback") || false)
|
|
91
|
+
|
|
92
|
+
step_bundles = resolve_step_bundles(steps)
|
|
93
|
+
|
|
94
|
+
Models::SimulationSession.new(
|
|
95
|
+
preset: preset_name,
|
|
96
|
+
source: sources,
|
|
97
|
+
steps: steps,
|
|
98
|
+
providers: providers,
|
|
99
|
+
repeat: repeat,
|
|
100
|
+
dry_run: dry_run,
|
|
101
|
+
writeback: writeback,
|
|
102
|
+
verbose: options[:verbose],
|
|
103
|
+
step_bundles: step_bundles,
|
|
104
|
+
synthesis_workflow: synthesis_workflow,
|
|
105
|
+
synthesis_provider: synthesis_provider
|
|
106
|
+
)
|
|
107
|
+
rescue ArgumentError => e
|
|
108
|
+
raise Ace::Support::Cli::Error.new(e.message)
|
|
109
|
+
rescue Ace::Sim::ValidationError => e
|
|
110
|
+
raise Ace::Support::Cli::Error.new(e.message)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def parse_steps(raw_steps)
|
|
114
|
+
raw_steps.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def resolve_step_bundles(steps)
|
|
118
|
+
steps.each_with_object({}) do |step, configs|
|
|
119
|
+
config_path = Ace::Sim.step_bundle_path(step)
|
|
120
|
+
if config_path.nil?
|
|
121
|
+
raise Ace::Support::Cli::Error.new("Missing step config for '#{step}' in .ace/sim/steps or defaults")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
configs[step] = config_path
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def pick_value(*values)
|
|
129
|
+
values.find { |value| !value.nil? }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def runner
|
|
133
|
+
@runner ||= Organisms::SimulationRunner.new
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
data/lib/ace/sim/cli.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ace/support/cli"
|
|
4
|
+
require "ace/core"
|
|
5
|
+
require_relative "cli/commands/run"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module Sim
|
|
9
|
+
module CLI
|
|
10
|
+
extend Ace::Support::Cli::RegistryDsl
|
|
11
|
+
|
|
12
|
+
PROGRAM_NAME = "ace-sim"
|
|
13
|
+
|
|
14
|
+
REGISTERED_COMMANDS = [
|
|
15
|
+
["run", "Run a preset simulation"]
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
HELP_EXAMPLES = [
|
|
19
|
+
"ace-sim run --preset validate-idea --source path/to/source.md --provider codex:mini --dry-run",
|
|
20
|
+
"ace-sim run --preset validate-idea --source path/to/source.md --provider codex:mini --provider google:gflash --repeat 2",
|
|
21
|
+
"ace-sim run --preset validate-idea --source path/to/source.md --provider glite --synthesis-workflow wfi://task/review --synthesis-provider claude:haiku"
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
def self.start(args)
|
|
25
|
+
Ace::Support::Cli::Runner.new(self).call(args: args)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
register "run", Commands::Run
|
|
29
|
+
|
|
30
|
+
version_cmd = Ace::Support::Cli::VersionCommand.build(
|
|
31
|
+
gem_name: "ace-sim",
|
|
32
|
+
version: Ace::Sim::VERSION
|
|
33
|
+
)
|
|
34
|
+
register "version", version_cmd
|
|
35
|
+
register "--version", version_cmd
|
|
36
|
+
|
|
37
|
+
help_cmd = Ace::Support::Cli::HelpCommand.build(
|
|
38
|
+
program_name: PROGRAM_NAME,
|
|
39
|
+
version: Ace::Sim::VERSION,
|
|
40
|
+
commands: REGISTERED_COMMANDS,
|
|
41
|
+
examples: HELP_EXAMPLES
|
|
42
|
+
)
|
|
43
|
+
register "help", help_cmd
|
|
44
|
+
register "--help", help_cmd
|
|
45
|
+
register "-h", help_cmd
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Sim
|
|
5
|
+
module Models
|
|
6
|
+
class SimulationSession
|
|
7
|
+
attr_reader :preset, :source, :steps, :providers, :repeat, :dry_run, :writeback, :run_id, :verbose,
|
|
8
|
+
:step_bundles, :synthesis_workflow, :synthesis_provider
|
|
9
|
+
|
|
10
|
+
def initialize(preset:, source:, steps:, providers:, repeat:, dry_run:, writeback:, verbose: false,
|
|
11
|
+
run_id: nil, step_bundles: {}, synthesis_workflow: nil, synthesis_provider: nil)
|
|
12
|
+
@preset = preset.to_s.strip
|
|
13
|
+
@source = Array(source).map(&:to_s).map(&:strip).reject(&:empty?)
|
|
14
|
+
@steps = Ace::Sim.normalize_list(steps)
|
|
15
|
+
@providers = Ace::Sim.normalize_list(providers)
|
|
16
|
+
@repeat = Integer(repeat)
|
|
17
|
+
@dry_run = !!dry_run
|
|
18
|
+
@writeback = !!writeback
|
|
19
|
+
@verbose = !!verbose
|
|
20
|
+
@step_bundles = stringify_step_bundles(step_bundles)
|
|
21
|
+
@synthesis_workflow = synthesis_workflow.to_s.strip
|
|
22
|
+
@synthesis_provider = synthesis_provider.to_s.strip
|
|
23
|
+
@run_id = run_id || Ace::Sim.next_run_id
|
|
24
|
+
|
|
25
|
+
validate!
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def dry_run?
|
|
29
|
+
dry_run
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def regenerate_run_id!
|
|
33
|
+
@run_id = Ace::Sim.next_run_id
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def bundle_path_for(step)
|
|
37
|
+
step_bundles[step.to_s]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def synthesis_enabled?
|
|
41
|
+
!synthesis_workflow.empty?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_h
|
|
45
|
+
{
|
|
46
|
+
"run_id" => run_id,
|
|
47
|
+
"preset" => preset,
|
|
48
|
+
"source" => source,
|
|
49
|
+
"steps" => steps,
|
|
50
|
+
"providers" => providers,
|
|
51
|
+
"repeat" => repeat,
|
|
52
|
+
"dry_run" => dry_run,
|
|
53
|
+
"writeback" => writeback,
|
|
54
|
+
"synthesis_workflow" => synthesis_workflow,
|
|
55
|
+
"synthesis_provider" => synthesis_provider
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def validate!
|
|
62
|
+
raise Ace::Sim::ValidationError, "source cannot be empty" if source.empty?
|
|
63
|
+
raise Ace::Sim::ValidationError, "steps cannot be empty" if steps.empty?
|
|
64
|
+
raise Ace::Sim::ValidationError, "providers cannot be empty" if providers.empty?
|
|
65
|
+
raise Ace::Sim::ValidationError, "repeat must be >= 1" if repeat < 1
|
|
66
|
+
raise Ace::Sim::ValidationError, "writeback cannot be enabled with --dry-run" if dry_run && writeback
|
|
67
|
+
if !synthesis_provider.empty? && synthesis_workflow.empty?
|
|
68
|
+
raise Ace::Sim::ValidationError, "synthesis_provider requires synthesis_workflow"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
missing_bundles = steps.reject { |step| step_bundles.key?(step) && !step_bundles[step].to_s.strip.empty? }
|
|
72
|
+
return if missing_bundles.empty?
|
|
73
|
+
|
|
74
|
+
raise Ace::Sim::ValidationError, "Missing step configs for: #{missing_bundles.join(", ")}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def stringify_step_bundles(raw)
|
|
78
|
+
raw.to_h.each_with_object({}) do |(step, path), acc|
|
|
79
|
+
acc[step.to_s] = path.to_s
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|