Background
pred uses Clap's derive API to define its command line. Clap automatically expands the hand-written CreateArgs definition into command-builder code; the model and rule registries do not currently generate this CLI definition. Although problem instance construction is already schema-driven through ProblemSchemaEntry, adding a model field still requires manually extending one global CreateArgs struct and mirroring the field in flag_map(), all_data_flags_empty(), and often a catalog-wide help block. Adding a reduction rule alone does not add create flags, but every new model or model field increases this central parser unless the CLI becomes genuinely schema-driven.
The repository currently has roughly 203 problem-schema registrations and 187 optional fields in CreateArgs. Clap constructs this complete command description before it parses any command, so even pred --version and pred list initialize all create arguments. This exceeded the default 1 MiB Windows main-thread stack and required the current 8 MiB worker-thread workaround in problemreductions-cli/src/main.rs. Adding more models continues to increase generated parser code, stack demand, central-file churn, and irrelevant pred create help.
Objective
Make pred create construct and parse arguments only for the selected problem schema. Adding a model with existing supported field types must not require adding fields or mappings to a catalog-wide CLI struct, and non-create commands must not initialize the problem-specific create parser.
Remove the 8 MiB worker-thread workaround once the parser itself passes the 1 MiB stack regression described below.
Repository-growth contract
The completed design must preserve these invariants as the catalog grows:
- The statically generated top-level Clap parser is independent of the number of registered models, model fields, and reduction rules.
pred create <PROBLEM> builds problem-specific Clap arguments proportional to the selected problem's fields, not the union of fields across the repository.
- Registering a new model whose field types are already supported requires only its normal model/schema/variant registration. It must not require editing a catalog-wide CLI field struct, flag map, empty-field predicate, or all-model help list.
- Registering a reduction rule between existing models must not change or rebuild the problem-specific
create argument set.
- Complete catalog traversal is allowed only for explicitly catalog-wide operations such as
pred list, schema export, or completion generation—not as hidden startup work for unrelated commands.
Interface (Input → Output)
In: Existing commands such as:
pred create MIS --graph 0-1,1-2
pred create SAT --num-vars 3 --clauses '1,2;-1,3'
pred create MIS/KingsSubgraph --positions '0,0;1,0;1,1'
pred create --example MIS/SimpleGraph/i32
Out: The same problem JSON and error behavior as today, except that problem-specific help and unknown-argument validation are scoped to the selected problem. For example, pred create MIS --help describes MIS inputs and pred create MIS --clauses ... is rejected by Clap as an argument that does not belong to MIS.
Global output flags must continue to work in their currently accepted positions, including after the problem-specific arguments.
Technical recommendations (non-binding)
- Keep the existing model, rule, solver, variant, and
ProblemSchemaEntry architecture. This issue does not require splitting the library into finer crates.
- Keep the statically derived top-level CLI, but defer initialization of
create (Command::defer / the corresponding derive support in Clap 4.6).
- Parse
create in two stages: first resolve the problem/variant and common modes such as --example; then build a clap::Command from only that schema's FieldInfo entries and parse the remaining arguments.
- Pass the selected field values to the existing schema-driven construction path as a field-name map. Adapt genuinely semantic parsers (graphs, matrices, dependencies, and similar formats) to read that map directly.
- Delete the superseded catalog-wide
CreateArgs fields, flag_map(), all_data_flags_empty(), and manually maintained all-problem flag help. Do not retain parallel old and new parsing paths.
- Generate complete static shell completions by traversing all schemas only when
pred completions is explicitly invoked, or use Clap's dynamic completion support. Ordinary command startup must not build the full create catalog.
Verification
Add a test named dynamic_create_parser_uses_bounded_stack and run:
cargo test -p problemreductions-cli dynamic_create_parser_uses_bounded_stack
The test must create a thread with an explicitly requested 1 MiB stack and, inside that thread, call the parser directly rather than invoking the pred executable. It must assert all of the following before joining successfully:
pred --version reaches Clap's normal DisplayVersion result without overflowing.
pred list parses successfully.
pred create MIS --graph 0-1,1-2 parses successfully.
pred create SAT --num-vars 3 --clauses '1,2;-1,3' parses successfully, proving that different schemas are selected correctly.
- As the negative control,
pred create MIS --clauses '1,2' returns clap::error::ErrorKind::UnknownArgument. This ensures the implementation did not obtain a passing stack test by collecting or ignoring arbitrary trailing arguments.
Add a second test named dynamic_create_parser_accepts_new_schema_without_cli_mapping and run:
cargo test -p problemreductions-cli dynamic_create_parser_accepts_new_schema_without_cli_mapping
This test must define a test-only synthetic schema with at least two uniquely named fields whose existing supported types differ, pass that schema directly through the same selected-schema command builder used in production, and successfully parse both canonical kebab-case flags. The synthetic field names must not appear in any production CLI struct, flag map, or match arm. As its negative control, the synthetic command must reject a flag belonging only to an unrelated real model.
The test must also iterate the registered problem schemas and assert that each selected command exposes the canonical kebab-case flag for every directly supplied schema field while excluding an unrelated problem's field. This makes future model additions exercise the generic path automatically instead of relying on a maintainer to remember a central CLI edit.
Then run the existing end-to-end suite:
cargo test -p problemreductions-cli --test cli_tests
It must pass with the worker-thread stack override removed from main.rs. The end-to-end tests must cover problem aliases, slash-qualified variants, --example, selected-problem help, and global output flags after model-specific arguments.
Finally run:
It must exit successfully.
Out of scope
- Splitting models or rules into additional crates.
- Changing reduction graph semantics or solver selection.
- Changing problem JSON formats or model field schemas.
- Using a larger linker stack or a still-larger worker-thread stack as the final fix.
Background
preduses Clap's derive API to define its command line. Clap automatically expands the hand-writtenCreateArgsdefinition into command-builder code; the model and rule registries do not currently generate this CLI definition. Although problem instance construction is already schema-driven throughProblemSchemaEntry, adding a model field still requires manually extending one globalCreateArgsstruct and mirroring the field inflag_map(),all_data_flags_empty(), and often a catalog-wide help block. Adding a reduction rule alone does not add create flags, but every new model or model field increases this central parser unless the CLI becomes genuinely schema-driven.The repository currently has roughly 203 problem-schema registrations and 187 optional fields in
CreateArgs. Clap constructs this complete command description before it parses any command, so evenpred --versionandpred listinitialize allcreatearguments. This exceeded the default 1 MiB Windows main-thread stack and required the current 8 MiB worker-thread workaround inproblemreductions-cli/src/main.rs. Adding more models continues to increase generated parser code, stack demand, central-file churn, and irrelevantpred createhelp.Objective
Make
pred createconstruct and parse arguments only for the selected problem schema. Adding a model with existing supported field types must not require adding fields or mappings to a catalog-wide CLI struct, and non-createcommands must not initialize the problem-specific create parser.Remove the 8 MiB worker-thread workaround once the parser itself passes the 1 MiB stack regression described below.
Repository-growth contract
The completed design must preserve these invariants as the catalog grows:
pred create <PROBLEM>builds problem-specific Clap arguments proportional to the selected problem's fields, not the union of fields across the repository.createargument set.pred list, schema export, or completion generation—not as hidden startup work for unrelated commands.Interface (Input → Output)
In: Existing commands such as:
Out: The same problem JSON and error behavior as today, except that problem-specific help and unknown-argument validation are scoped to the selected problem. For example,
pred create MIS --helpdescribes MIS inputs andpred create MIS --clauses ...is rejected by Clap as an argument that does not belong to MIS.Global output flags must continue to work in their currently accepted positions, including after the problem-specific arguments.
Technical recommendations (non-binding)
ProblemSchemaEntryarchitecture. This issue does not require splitting the library into finer crates.create(Command::defer/ the corresponding derive support in Clap 4.6).createin two stages: first resolve the problem/variant and common modes such as--example; then build aclap::Commandfrom only that schema'sFieldInfoentries and parse the remaining arguments.CreateArgsfields,flag_map(),all_data_flags_empty(), and manually maintained all-problem flag help. Do not retain parallel old and new parsing paths.pred completionsis explicitly invoked, or use Clap's dynamic completion support. Ordinary command startup must not build the full create catalog.Verification
Add a test named
dynamic_create_parser_uses_bounded_stackand run:cargo test -p problemreductions-cli dynamic_create_parser_uses_bounded_stackThe test must create a thread with an explicitly requested 1 MiB stack and, inside that thread, call the parser directly rather than invoking the
predexecutable. It must assert all of the following before joining successfully:pred --versionreaches Clap's normalDisplayVersionresult without overflowing.pred listparses successfully.pred create MIS --graph 0-1,1-2parses successfully.pred create SAT --num-vars 3 --clauses '1,2;-1,3'parses successfully, proving that different schemas are selected correctly.pred create MIS --clauses '1,2'returnsclap::error::ErrorKind::UnknownArgument. This ensures the implementation did not obtain a passing stack test by collecting or ignoring arbitrary trailing arguments.Add a second test named
dynamic_create_parser_accepts_new_schema_without_cli_mappingand run:cargo test -p problemreductions-cli dynamic_create_parser_accepts_new_schema_without_cli_mappingThis test must define a test-only synthetic schema with at least two uniquely named fields whose existing supported types differ, pass that schema directly through the same selected-schema command builder used in production, and successfully parse both canonical kebab-case flags. The synthetic field names must not appear in any production CLI struct, flag map, or match arm. As its negative control, the synthetic command must reject a flag belonging only to an unrelated real model.
The test must also iterate the registered problem schemas and assert that each selected command exposes the canonical kebab-case flag for every directly supplied schema field while excluding an unrelated problem's field. This makes future model additions exercise the generic path automatically instead of relying on a maintainer to remember a central CLI edit.
Then run the existing end-to-end suite:
cargo test -p problemreductions-cli --test cli_testsIt must pass with the worker-thread stack override removed from
main.rs. The end-to-end tests must cover problem aliases, slash-qualified variants,--example, selected-problem help, and global output flags after model-specific arguments.Finally run:
make checkIt must exit successfully.
Out of scope