The shell operation surface

Table of Contents

1. Summary

The shell gives the synthetic commands one menu, built by synthetic_commands::register_commands in projects/ores.shell/application/src/app/commands/synthetic_commands.cpp. The verbs mirror the Market Simulator window, so an operator who cannot run the Qt client still starts, stops and inspects feeds. Nothing in the group names an asset class, and the folder verbs cross asset classes because the marketdata service owns the fan-out.

2. Detail

2.1. The verbs

synthetic list folders lists the folder hierarchy visible to the logged in party. It takes the flags --config-id to scope the listing to one collection and --name to filter by name.

synthetic list feeds lists every configured feed of every asset class, whether or not the service is running it. It takes no arguments and no asset class flag. Each line states the feed's class, its source name, whether it is enabled, whether it is set to auto-start, whether it is running now, and the child rows it holds; a feed missing a child row is marked [partial], so an incomplete feed is visible rather than absent. The one fact no config row carries is what is running, so the command joins the config families against the runner's running set by generator id.

synthetic setup authors a whole feed in one pass, of the class --kind names: for FX the collection, the folder chain, the generation config, the FX spot config and its GMM components; for IR the same container and chain, the IR curve config, its Curve Template entries and its process parameter values. Every reference the run depends on is checked before the first row is written, so a missing process type, parameter or required flag is named and nothing is half authored. The flow, its order and its reporting are shared; the class supplies only which rows a feed needs.

synthetic preview fx-spot, synthetic preview ir-shape and synthetic preview ir-paths take one feed token and reach the three dry-run operations the service already offers: synthetic.v1.ops.simulate_fx_spot_paths, synthetic.v1.ops.preview_ir_curve_shape and synthetic.v1.ops.simulate_ir_curve_paths. Each states the feed's own process type and its stored rows, so the preview is of the configured feed and not of a request the caller assembles. fx-spot and ir-paths take --ticks, --paths and --seed; ir-shape takes --seed and --frequency.

synthetic start and synthetic stop take one target, written as folder <token> or feed <token>.

2.2. marketdata stream

marketdata stream <oresmd-uri> watches one datum on the republished tick stream. It resolves the URI with the oresmd codec, writes the datum's canonical ORE key, and subscribes on the caller's own tenant and party subject, so the command takes no tenant or party argument. Each tick prints its observation time, the point's oresmd URI, the value and the source; --csv <path> writes the same four fields as a header and one row per tick instead. Ctrl-C stops the stream, prints the count, and leaves the file flushed.

An unparseable or unknown URI is refused with the codec's own message, naming what the grammar did not admit rather than a subject error. A series URI states two subjects, because the series key and the point keys under it are different subjects and no NATS wildcard covers both: the series subject and the point subject one token down.

2.3. The target rule

A folder token is a UUID, an exact folder name, or a standard codename path such as synthetic/2026realistic/fx. The matching applies slugify to both sides, which lowercases the value and removes spaces, so 2026 Realistic and 2026realistic are the same component. A leading slash is ignored, and absolute and relative paths resolve to the same folder.

A bare codename matches a folder at any depth. Two folders named fx under different collections therefore produce an ambiguous match, and the command prints the candidates instead of choosing one.

A feed token is a UUID, an oresmd URI, or a source_name. resolve_feed probes the FX spot config family first and the IR curve family second. A UUID is matched against the config ids in that same order. A name is matched against an FX config's oresmd URI or source name, and then against an IR curve config's source name only, because an IR curve config names several series and not one URI. A token shared by several visible feeds is reported as ambiguous, with each feed's source_name listed.

The probe order follows the server. The service resolves a kind by probing the repositories in the same order, so a token that could name either family resolves to the feed the server would start.

2.4. What a target becomes on the wire

synthetic start folder sends marketdata::messaging::start_feeds_under_folder_request with the resolved folder id. The marketdata service fans the request out over the folder and replies with a started count, an already running count and a skipped count. synthetic start feed sends synthetic::messaging::start_feed_request with the config id. Stop mirrors both with the stop requests.

The client never derives a source_name. The per-feed request is keyed by config_id and the server resolves it to the config's own source_name.

2.5. Facts the code shape does not show

  • The group is kind agnostic by construction. No verb takes an asset class, and the folder verbs work for a mix of kinds because the marketdata service, not the shell, walks the folder.
  • The shell talks to two services for one subject. The folder verbs are marketdata requests and the per-feed verbs are synthetic requests. That split follows the folder tree, which the marketdata side owns.
  • synthetic list feeds reads the config families and asks the service what is running, then joins the two by source_name. A stopped feed and a config that was never started therefore still appear, marked [stopped], which is what makes a partial feed visible rather than absent.
  • synthetic setup is the one verb that writes more than one row. It builds the whole plan before it sends the first row, and every row it sends is the request the matching generated add verb sends, on the same subject, so the porcelain flow is not a second writer for a row the generated menus already write. It is not transactional: a failure part way leaves the rows already written, and the command says how many were not.
  • The command group is the surface a script uses, so its verb names and target grammar are a contract. The proposal keeps this note in step with synthetic_commands.cpp in the same change as any new verb.

3. See also

Emacs 29.3 (Org mode 9.6.15)