Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create a MATLAB baseline regression test class for a Simulink model. Use when asked to create a baseline test, golden-reference test, or regression test for a Simulink model.
.claude/skills/hashgraph-online-simulink-baseline-test/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -4% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 9% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 12% | 0% |
Creates a MATLAB test class that captures and validates Simulink model outputs against a saved baseline.
Resolve the model name and explore its structure to select 2–5 signals representative of the model's key behavior.
matlabmodelName = bdroot(gcs); % List top-level subsystems opts = Simulink.FindOptions; opts.SearchDepth = 1; topBlocks = getfullname(Simulink.findBlocks(modelName, opts)); for i = 1:numel(topBlocks) bt = get_param(topBlocks{i}, 'BlockType'); fprintf(' [%s] %s\n', bt, topBlocks{i}); end
Explore subsystem ports, bus selectors, scopes, and outports to understand signal flow. Look for signals that best represent the model's overall behavior — plant outputs, actuator commands, sensor readings, or any other meaningful quantities.
Enable logging on selected output ports:
matlabph = get_param('<block path>', 'PortHandles'); set(ph.Outport(1), 'DataLogging', 'on'); set(ph.Outport(1), 'Name', '<signalName>');
Never use To Workspace blocks for signal capture.
Simulate and save the full logsout Dataset — not individual timeseries:
matlabin = Simulink.SimulationInput(modelName); out = sim(in); baselineLogsout = out.logsout; save(fullfile(modelDir, 'baselineData.mat'), 'baselineLogsout');
Follow these mandatory conventions:
sltest.TestCasematlabclassdef MyModelBaselineTest < sltest.TestCase
Not matlab.unittest.TestCase. This gives access to verifySignalsMatch.
Simulate the model once. Compare all signals in one call using verifySignalsMatch:
matlabtestCase.verifySignalsMatch(out.logsout, S.baselineLogsout, ... 'RelTol', testCase.RelTol, ... 'AbsTol', testCase.AbsTol);
Never loop through individual signals. Never simulate once per signal.
RelTol and AbsTol directlyPass 'RelTol' and 'AbsTol' as name-value arguments. MATLAB applies RelTol element-wise per sample; AbsTol acts as a floor for values near zero. Never manually scale tolerances (e.g., max(abs(data)) * relTol).
bdIsLoadedOnly close the model if it was not already loaded before the test:
matlabwasLoaded = bdIsLoaded(testCase.ModelName); load_system(testCase.ModelName); if ~wasLoaded testCase.addTeardown(@()close_system(testCase.ModelName, 0)); end
PreLoadFcnload_system automatically triggers the model's PreLoadFcn callback. Never call evalin('base', get_param(model, 'PreLoadFcn')).
generateBaseline() methodFor easy re-baselining after intentional changes:
matlabmethods (Static) function generateBaseline() modelName = MyModelBaselineTest.ModelName; load_system(modelName); in = Simulink.SimulationInput(modelName); out = sim(in); baselineLogsout = out.logsout; %#ok<NASGU> save(MyModelBaselineTest.BaselineFile, 'baselineLogsout'); close_system(modelName, 0); end end
Execute the test and confirm all signals match:
matlabresults = runtests('MyModelBaselineTest');
matlabclassdef MyModelBaselineTest < sltest.TestCase properties (Constant) ModelName = 'MyModel' BaselineFile = fullfile(fileparts(mfilename('fullpath')), 'baselineData.mat') RelTol = 1e-6 AbsTol = 1e-8 end methods (TestClassSetup) function loadModelAndBaseline(testCase) wasLoaded = bdIsLoaded(testCase.ModelName); load_system(testCase.ModelName); if ~wasLoaded testCase.addTeardown(@()close_system(testCase.ModelName, 0)); end testCase.assertTrue(isfile(testCase.BaselineFile), ... sprintf('Baseline not found: %s\nRun %s.generateBaseline() first.', ... testCase.BaselineFile, mfilename('class'))); end end methods (Test) function testAllSignalsMatchBaseline(testCase) in = Simulink.SimulationInput(testCase.ModelName); out = sim(in); S = load(testCase.BaselineFile, 'baselineLogsout'); testCase.verifySignalsMatch(out.logsout, S.baselineLogsout, ... 'RelTol', testCase.RelTol, ... 'AbsTol', testCase.AbsTol); end end methods (Static) function generateBaseline() modelName = MyModelBaselineTest.ModelName; load_system(modelName); in = Simulink.SimulationInput(modelName); out = sim(in); baselineLogsout = out.logsout; %#ok<NASGU> save(MyModelBaselineTest.BaselineFile, 'baselineLogsout'); fprintf('Baseline saved to: %s\n', MyModelBaselineTest.BaselineFile); close_system(modelName, 0); end end end
sim each time)verifySignalsMatchmax(abs(data)) * relTol) — use 'RelTol' directlylogsout DatasetbdIsLoaded firstPreLoadFcn — load_system handles itmatlab.unittest.TestCase — use sltest.TestCase for verifySignalsMatch| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | pass→pass | 19,836 | 16,798 | -15% | 1 | 1 | 0% | 2,670 | 3,509 | +31% | 0 | 0 | — |
case-01 | fail→pass | 21,286 | 18,224 | -14% | 1 | 1 | 0% | 3,465 | 4,327 | +25% | 0 | 0 | — |
case-02 | fail→pass | 21,216 | 15,759 | -26% | 1 | 1 | 0% | 4,228 | 4,073 | -4% | 0 | 0 | — |
case-03 | fail→pass | 20,969 | 10,550 | -50% | 1 | 1 | 0% | 4,024 | 3,669 | -9% | 0 | 0 | — |
case-04 | pass→pass | 21,833 | 15,167 | -31% | 1 | 1 | 0% | 3,280 | 4,795 | +46% | 0 | 0 | — |
case-05 | pass→pass | 15,812 | 17,439 | +10% | 1 | 1 | 0% | 3,004 | 4,768 | +59% | 0 | 0 | — |
case-06 | pass→pass | 14,132 | 18,544 | +31% | 1 | 1 | 0% | 2,625 | 4,242 | +62% | 0 | 0 | — |
case-07 | pass→pass | 17,225 | 7,943 | -54% | 1 | 1 | 0% | 2,175 | 3,002 | +38% | 0 | 0 | — |
case-08 | fail→pass | 18,945 | 10,738 | -43% | 1 | 1 | 0% | 2,336 | 2,539 | +9% | 0 | 0 | — |
case-09 | fail→pass | 17,340 | 12,568 | -28% | 1 | 1 | 0% | 2,739 | 3,067 | +12% | 0 | 0 | — |
case-10 | fail→pass | 27,176 | 9,244 | -66% | 1 | 1 | 0% | 1,618 | 3,350 | +107% | 0 | 0 | — |
case-11 | fail→pass | 15,259 | 11,262 | -26% | 1 | 1 | 0% | 1,897 | 2,640 | +39% | 0 | 0 | — |
case-12 | pass→pass | 14,866 | 9,940 | -33% | 1 | 1 | 0% | 1,662 | 2,315 | +39% | 0 | 0 | — |
case-13 | fail→pass | 19,229 | 11,899 | -38% | 1 | 1 | 0% | 2,515 | 2,940 | +17% | 0 | 0 | — |
case-15 | pass→pass | 13,541 | 12,426 | -8% | 1 | 1 | 0% | 1,676 | 2,658 | +59% | 0 | 0 | — |
case-16 | fail→pass | 19,586 | 12,604 | -36% | 1 | 1 | 0% | 3,359 | 3,175 | -5% | 0 | 0 | — |
case-17 | fail→pass | 17,397 | 6,590 | -62% | 1 | 1 | 0% | 2,182 | 2,629 | +20% | 0 | 0 | — |
case-18 | fail→pass | 11,804 | 9,093 | -23% | 1 | 1 | 0% | 1,909 | 2,770 | +45% | 0 | 0 | — |
case-19 | pass→pass | 18,891 | 10,960 | -42% | 1 | 1 | 0% | 2,795 | 2,826 | +1% | 0 | 0 | — |
case-20 | pass→pass | 18,085 | 8,255 | -54% | 1 | 1 | 0% | 3,171 | 3,010 | -5% | 0 | 0 | — |
case-21 | fail→pass | 22,915 | 8,482 | -63% | 1 | 1 | 0% | 1,244 | 2,032 | +63% | 0 | 0 | — |
case-22 | fail→pass | 39,827 | 12,710 | -68% | 1 | 1 | 0% | 2,235 | 3,121 | +40% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +59 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.