Root cause
The rejection occurs in:
src/app/domain/schema-management/services/generation/base.strategy.ts
if (mode === 'DYNAMIC' && method === 'MINIMIZATION') {
throw new CodeGenerationError(
'Dynamic simulation engine is not supported for Pocock-Simon Minimization. Please use Static Manifest mode.',
config
);
}
This is not caused by StaticMappingGuard; that guard only validates output after generation succeeds.
The modal currently allows users to choose both Dynamic Generator and Both (ZIP Bundle) for a minimization study:
DYNAMIC calls dynamic generation and displays an error panel.
BOTH generates static output first, then dynamic output; dynamic generation throws, so the otherwise-valid static artifact is discarded.
- Downloading
BOTH repeats that same failing dynamic-generation call, so no ZIP is produced.
Why dynamic minimization is not a small template change
The current dynamic-generator IR and language strategies are built around block randomization:
- precomputed site/stratum/cap tasks;
- block-size selection;
- block construction and Fisher–Yates shuffling;
- a round-robin loop until each task reaches its cap.
Pocock-Simon minimization instead needs state that the IR does not represent:
- sequential subject generation up to
minimizationConfig.totalSampleSize;
- sampling of every stratification factor level according to
expectedProbability;
- per-site, per-factor, per-level, per-arm marginal counts;
- candidate-arm imbalance scoring relative to treatment ratios;
- biased-coin selection using
minimizationConfig.p;
- cap eligibility and the algorithm’s existing truncation behavior;
- deterministic MT19937 draw ordering compatible with the application engine.
The application-side implementation already exists in:
src/app/domain/randomization-engine/core/minimization-algorithm.ts
It is covered by unit, property, golden-fixture, and worker/fallback parity tests. Static code generation works because it runs that implementation first and exports the resulting concrete manifest.
Proposed split
1. Immediate UX fix — prevent a dead-end export flow
Update CodeGeneratorModalComponent to derive whether dynamic export is supported from the active config.
For randomizationMethod === 'MINIMIZATION':
- disable the Dynamic Generator and Both (ZIP Bundle) segmented controls;
- add an explanatory tooltip or inline note:
“Dynamic export is not yet available for Pocock-Simon minimization. A static manifest preserves the generated allocation sequence.”
- if a stale persisted selection is
DYNAMIC or BOTH, automatically set exportMode to STATIC, regenerate the preview, and announce the fallback accessibly;
- defensively normalize to
STATIC in refreshCode() and downloadCode() as well, so programmatic calls and restored UI state cannot reach the unsupported path;
- do not silently produce a ZIP containing only a static file while labeling it “Both.”
This resolves the user-facing bug without misrepresenting dynamic-engine support.
2. Long-term dynamic-engine support — separate feature work
Implement a minimization-specific dynamic strategy rather than forcing the existing block strategy to handle it:
-
Extend LogicIR with a discriminated minimization payload:
- total sample size;
- sites and allocation behavior;
- factors, levels, and expected probabilities;
- cap strategy / cap data;
- arm IDs, names, normalized ratios;
- biased-coin probability;
- explicit RNG-consumption contract.
-
Add a minimization dynamic-logic builder to the algorithm registry/framework.
-
Add language-specific implementations for R, Python, SAS, and Stata that maintain their own local mutable marginal-count state.
-
Preserve the application algorithm’s semantics:
- eligible-arm selection;
- ratio-aware imbalance calculation;
- tie handling;
- biased-coin selection;
- subject-ID sequence behavior;
- deterministic seeded output.
-
Replace the current minimization rejection test with per-language dynamic-generation and behavioral-parity tests.
Acceptance criteria
UX fallback
Dynamic-engine support
Suggested priority
I recommend treating the UX fallback as the immediate bug fix, and tracking dynamic minimization parity as a separate, larger feature. The latter is a multi-language port of a stateful allocation algorithm and should not be hidden behind a small “remove the guard” change.
Root cause
The rejection occurs in:
src/app/domain/schema-management/services/generation/base.strategy.tsThis is not caused by
StaticMappingGuard; that guard only validates output after generation succeeds.The modal currently allows users to choose both Dynamic Generator and Both (ZIP Bundle) for a minimization study:
DYNAMICcalls dynamic generation and displays an error panel.BOTHgenerates static output first, then dynamic output; dynamic generation throws, so the otherwise-valid static artifact is discarded.BOTHrepeats that same failing dynamic-generation call, so no ZIP is produced.Why dynamic minimization is not a small template change
The current dynamic-generator IR and language strategies are built around block randomization:
Pocock-Simon minimization instead needs state that the IR does not represent:
minimizationConfig.totalSampleSize;expectedProbability;minimizationConfig.p;The application-side implementation already exists in:
src/app/domain/randomization-engine/core/minimization-algorithm.tsIt is covered by unit, property, golden-fixture, and worker/fallback parity tests. Static code generation works because it runs that implementation first and exports the resulting concrete manifest.
Proposed split
1. Immediate UX fix — prevent a dead-end export flow
Update
CodeGeneratorModalComponentto derive whether dynamic export is supported from the active config.For
randomizationMethod === 'MINIMIZATION':“Dynamic export is not yet available for Pocock-Simon minimization. A static manifest preserves the generated allocation sequence.”
DYNAMICorBOTH, automatically setexportModetoSTATIC, regenerate the preview, and announce the fallback accessibly;STATICinrefreshCode()anddownloadCode()as well, so programmatic calls and restored UI state cannot reach the unsupported path;This resolves the user-facing bug without misrepresenting dynamic-engine support.
2. Long-term dynamic-engine support — separate feature work
Implement a minimization-specific dynamic strategy rather than forcing the existing block strategy to handle it:
Extend
LogicIRwith a discriminated minimization payload:Add a minimization dynamic-logic builder to the algorithm registry/framework.
Add language-specific implementations for R, Python, SAS, and Stata that maintain their own local mutable marginal-count state.
Preserve the application algorithm’s semantics:
Replace the current minimization rejection test with per-language dynamic-generation and behavioral-parity tests.
Acceptance criteria
UX fallback
CodeGenerationError.Dynamic-engine support
p, total sample size, factor-level expected probabilities, treatment ratios, sites, and supported caps.generateMinimization().Suggested priority
I recommend treating the UX fallback as the immediate bug fix, and tracking dynamic minimization parity as a separate, larger feature. The latter is a multi-language port of a stateful allocation algorithm and should not be hidden behind a small “remove the guard” change.