Matlab Engineer Tabular Features
MathWorksOfficialCreated by MathWorks
- Skill
- Workflow
- Data and AI
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a rigorous, data-driven workflow for feature engineering and selection in MATLAB. It manages the entire process from initial data intake and screening to feature pool generation, consensus-based selection, and diagnostic assessment. Designed for single-response supervised learning, it handles both tabular and non-tabular data by routing complex extractions to domain-specific skills. The workflow emphasizes train-test discipline, ensuring that feature generation and selection are performed on training data while maintaining a held-out set for unbiased evaluation. It concludes by delivering a self-contained inference script that allows users to reproduce the selected feature set on new data.
What it does
- Screens raw data to remove degenerate or constant predictors.
- Generates a candidate feature pool using either SMLT or domain-specific extractors.
- Performs consensus-based feature selection using a ranker panel and elbow-cut logic.
- Assesses feature set quality and stability through performance metrics and resampling.
- Produces a standalone MATLAB inference script for applying the selected features to new data.
- Generates comprehensive reports on the selection process and model-free feature quality.
When to use it
- When you need to engineer and select the best predictors for a single-response classification or regression task.
- When you require a structured, reproducible selection process rather than ad-hoc feature picking.
- When working with non-tabular data that requires specialized extraction before feature selection.
- When you need a reliable, self-contained script to apply your feature engineering pipeline to future datasets.
Advantages
- Enforces strict train-test discipline to prevent data leakage.
- Provides a consensus-driven approach to feature selection that reduces bias.
- Offers diagnostic tools to evaluate feature set stability and predictive performance.
- Delivers a portable inference script that encapsulates the entire transformation logic.
Usage examples
- “I have a table of sensor data and want to select the best features for a regression model.”
- The agent will initiate the intake phase, confirm your output directory, and guide you through the feature engineering and selection workflow.
- “Can you help me select features for my classification task and check if they are stable?”
- The agent will perform the selection and then offer to run stability gates to see if the same features are chosen across resampled data.
Install
8 of its files are not mirrored here, so install it from its source:
https://github.com/matlab/matlab-agentic-toolkit/tree/2a7f215e965597acf6ed495beb9e7b8aff45a3be/skills-catalog/ai-and-statistics/matlab-engineer-tabular-features
Documentation by MathWorks
Engineer Tabular Features
A lean, functional pipeline: intake → feature pool → select → assess →
deliver → report. There is no shared context object — each phase is a direct
call to leaf utilities in scripts/, and the reference file for each phase
carries the detail. Your value is the structured, data-driven process and,
above all, the consensus selection at its center — not an ad-hoc answer.
This skill bundles the workflow in references/ (per-phase detail read on demand)
and scripts/ (the computation and plotting utilities). Do not invoke files in
references/ as separate skills — they are loaded only via the Read tool when
the phase that needs them runs. The per-phase files call the pipeline's leaf helpers
for you; if you ever need a helper's signature,
references/internal-helpers.md documents each one's
inputs, outputs, and an example call — so you never open a helper's source.
When to Use
Engineering or selecting the best predictors for single-response supervised classification or regression on a plain in-memory
table.You want a structured, data-driven selection — a ranker panel, a consensus vote, and an elbow cut — rather than an ad-hoc hand-picked feature set.
The data is non-tabular (signals, images, battery/machinery telemetry): this skill routes extraction to the matching domain skill, then engineers, selects, assesses, and delivers on the resulting table (see references/domain-routing.md).
When NOT to Use
Multi-response problems — this skill is single-response only.
Model training, tuning, or deployment — it prepares features and stops. Hand the delivered table to a model-training/classification workflow to fit and compare models.
Raw data acquisition. And for the extraction step on non-tabular data, the actual feature computation belongs to the matching domain extraction skill — this skill orchestrates that handoff (see references/domain-routing.md), it does not re-implement it.
Requires the Statistics and Machine Learning Toolbox (SMLT) —
gencfeatures/genrfeatures build the pool and the ranker/assessment utilities
are SMLT-based. MATLAB Report Generator is optional (enables the PDF report;
markdown is always produced).
Running MATLAB
Run all MATLAB through the MATLAB MCP server (mcp__matlab__evaluate_matlab_code,
or mcp__matlab__run_matlab_file for scripts). Set project_path to this skill's
scripts/ directory so the utilities resolve on the current working folder
without any addpath calls. Every utility is a leaf function called directly —
there is no initialization step and no context object to construct. Validate any
code you author with mcp__matlab__check_matlab_code before running it.
Start each dataset from scratch — but use the live workspace within a run. The
MCP session is stateful, so a run's intermediates should live in the workspace: set
RawTbl, Splits, FullEng, SelectedNames, Baseline, etc. once and pass them
phase-to-phase. Do not round-trip them through save/load .mat files (noise,
risks stale reads) and do not addpath. Across different datasets/runs, carry
nothing — begin each analysis by setting every variable afresh.
Communication style while running this skill
Talk to the user about their data and results, not the skill's plumbing.
Everything under references/ and scripts/ is internal. Rule of thumb: if a
sentence would only make sense to someone who has read this skill's source files,
don't say it.
Never name internal files, helpers, or phase/gating mechanics.
runConsensusSelection,GenInfo.BinaryReliant, "the redundancy dimension", etc. are internal — give the outcome ("these features duplicate each other, so I'm keeping the strongest"), not the mechanism. Name an internal only when it is a problem the user can act on. Read reference files silently.Use plain words for each check. The three assessment reads: performance → whether the new features improve predictions (a held-out estimate, or a cross-validated mean ± std); fixed-pool stability → whether the same features get picked when rows are resampled; generation stability → whether the same features get built and picked when the whole pipeline re-runs on resampled rows. Say "the ranking step" not "the borda voter"; "reliably re-selected" not "consensus core".
Don't narrate uncertainty or mid-flight course-corrections — settle how a function is called silently, then report only the outcome. Surface a difficulty only when the user must decide on it.
Announce cost before long work, one sentence — pool size before selection, expected time before a K-fold, and before either stability gate (both re-run selection many times; the generation gate also re-builds the pool each time). And surface user-facing questions verbatim where a phase specifies one (output directory, wide-input, domain routing).
Report what was dropped at every phase (screened predictors, excluded WoE columns, skipped rankers) — a silent shrink reads as data loss. But selection evaluates the pool, it doesn't necessarily shrink it — never call it a reduction.
Output directory — REQUIRED, HARD HALT
Deliverables are written to disk. Always confirm the output directory with the user before writing anything. Do not assume the working directory, do not create one silently.
The pipeline
Follow the phases in order. Each links to its reference; read the reference before executing the phase.
1. Intake — references/intake.md
Ask before running any code. Intake is a required conversation, not a
default-fill. Confirm every run parameter with the user before proceeding past
the screen — data source, response, dataset name, output directory (hard-halt),
domain description, separate-test-set, model family (+ lens if agnostic),
evaluation strategy, report opt-out — asked one at a time, in the order pinned in
intake.md §1 (never dump the whole list in one message).
Every item must be asked; offer a default where one exists, but confirm rather than
assume — when the opening request implies an answer, state what you inferred and
have the user confirm it. Two are
non-negotiable — do not proceed without an explicit answer:
Output directory — the disk-write hard-halt (see above).
Domain description — the sole input to domain routing. Ask what the data is and where it came from; route to a domain extractor if one fits, else the generic path. Never infer the domain from column names or fall through to generic generation on silence. Verbatim prompt in intake.md.
Then assemble the data into one plain table, briefly confirm what was
loaded (shape, response, problem type), and screen degenerate predictors. A
timetable/tall/gpuArray/datastore isn't a dead end — it's a signal to run a
tabularizing step first (often a domain skill, see
domain-routing.md) and then re-enter intake with
the resulting table; only halt if no tabular path exists.
[ScreenedTbl, ScreenInfo] = screenPredictors(RawTbl, Response); % or (X, y)screenPredictors handles the polymorphic response (a name already in the table,
or a separately-supplied vector/table it concatenates) and drops constant and
near-empty predictors. Then profile, reserve the user's untouched slice, and split:
Profile = profileForSplit(ScreenedTbl, ScreenInfo.ResponseVar);
[WorkingIdx, UserHeldOutIdx, ReserveInfo] = reserveHoldoutForUser( ...
ScreenedTbl, Profile.ProblemType, ScreenInfo.ResponseVar, ReserveForUser = HasNoSeparateTest);
[Splits, SplitDecision] = splitStrategy(ScreenedTbl, Profile.ProblemType, ...
ScreenInfo.ResponseVar, Subset = WorkingIdx, EvaluationStrategy = EvaluationStrategy);reserveHoldoutForUser sets aside an untouched slice for the user's own testing
when they have no separate test set (default 20%, user-settable via HoldoutFraction;
else nothing); the rest is the working data all phases run on. When a carve
happens, materialize the slice as the table variable's name + _test from the original
rows and narrate the split in plain words (fraction, row counts, and method —
stratified/random — from ReserveInfo); see intake.md. splitStrategy then sets
Splits.TrainIdx/.TestIdx over the working rows — a train/test split under
holdout, or all working rows with empty TestIdx under cross_validated.
Generation and selection run on TrainIdx only.
2. Feature pool — references/feature-pool.md
Produce the candidate pool. First check whether a domain skill fits the data (references/domain-routing.md); otherwise use the default SMLT path:
Opts.TargetModel = TargetModel; Opts.Standardization = "auto";
OptArgs = namedargs2cell(Opts);
TrainIdx = Splits.TrainIdx; TestIdx = Splits.TestIdx;
TrainTbl = ScreenedTbl(TrainIdx, :); % fit generation on train rows only
[~, Transformer, GenInfo] = generateFeatures(TrainTbl, Response, ProblemType, OptArgs{:});
Recipe = Transformer; % SMLT recipe (domain path: the captured struct)
FullEng = transformFeatures(Recipe, ScreenedTbl); % engineered pool over ALL rows
FullEng.(Response) = ScreenedTbl.(Response); % transformFeatures returns predictors onlyGenerate-only (external consensus does the cutting). Mind the wide-input guard
(generateFeatures:tooManyPredictors) — hold the wide-input conversation and
re-call with Opts.NumFeatures set. FullEng is the canonical pool: engineered
over all rows with the response re-attached, train-fit so the held-out rows
stay leakage-clean. On the domain path the captured table already spans all
rows — use it as FullEng directly. Whichever path runs, downstream reads only
the pool contract (Recipe, describeFeatures, transformFeatures) — never
the producer. Announce GenInfo.PoolSize.
Set OriginalData/OriginalPredVars here — the baseline's "original" reference is
path-dependent (raw columns when they exist, else the full pool). assess.md §1 pins the rule.
3. Select — references/select.md
The heart of the skill. One call runs the ranker panel, the consensus vote, and the elbow cut:
[SelectedNames, VoteTable, PanelInfo] = runConsensusSelection( ...
FullEng(TrainIdx, :), Response, ProblemType, ExcludeFeatures = GenInfo.BinaryReliant, ...
TargetModel = TargetModel);Selection runs on the training rows only — slice FullEng(TrainIdx,:); the
held-out rows never enter ranking. TargetModel gates the ranker panel: a declared
family runs its own embedded probe (linear→lasso, tree_ensemble→oob,
kernel_distance→nca) plus the two model-agnostic rankers; agnostic (the default)
keeps the full five-ranker panel. Report PanelInfo.Reasoning. Build a
plotSelectionConsensus figure only when the report is on (GenerateReport) —
every figure is a report input, so a report opt-out skips all plot calls.
The score elbow alone sets the count (see select.md); when the scores decline too gradually to show a knee the full pool is kept — report a keep-all as "the candidates are comparably useful," not as a failure to select.
4. Assess — references/assess.md
Diagnostic only; never revises the delivered set. The performance read follows the evaluation strategy chosen at intake:
Quality = featureSetQuality(ScreenedTbl, FullEng, TrainIdx, Response, ...
OriginalPredVars, SelectedNames, ProblemType); % model-free, always
if Splits.Strategy == "holdout" % point estimate on the held slice
Baseline = baselineComparison(ScreenedTbl, FullEng, TrainIdx, TestIdx, Response, ...
OriginalPredVars, SelectedNames, ProblemType, TargetModel = TargetModel, InternalCV = false);
else % cross_validated: folds ARE the estimate
KFold = assessKFold(ScreenedTbl(TrainIdx, :), Response, ProblemType, CVP, TargetModel = TargetModel);
end
% Stability — two independent opt-in gates, both default OFF (assess.md §3):
rng(0, "twister");
SelStab = assessSelectionStability(StabEng, Response, ProblemType, ... % Gate 1 (fixed pool)
ExcludeFeatures = StabExclude, TargetModel = TargetModel, MaxRows = RowBudget);
GenStab = assessGenerationStability(RawTrainTbl, Response, ProblemType, ... % Gate 2 (pool regenerated)
TargetModel = TargetModel, MaxRows = RowBudget);Pass a row budget to both gates as MaxRows only when the user named one
("~1000 patients is fine" → MaxRows = 1000) to keep the audit affordable; else
omit it (default 3000) — don't pass an empty value.
Quality (model-free representation quality) and the strategy's performance read
are unconditional: under holdout, baselineComparison scores the held
TestIdx as a point estimate (InternalCV = false, no error bars); under
cross_validated (TestIdx empty), assessKFold is the headline (regenerates per
fold, reports mean ± std, performance-only) — skip baselineComparison.
Stability is two independent opt-in gates, both default OFF (see
assess.md §3): Gate 1 SelStab (re-selects on a fixed
pool) and Gate 2 GenStab (also regenerates the pool per subsample; needs a
re-runnable fitter — SMLT by default, a domain path needs Recipe.Fit, §3c). Both
are computationally expensive — each re-runs the consensus selection M times, and
Gate 2 adds a full regeneration on top — so both stay off unless asked for. Both
grade the procedure; neither revises the delivered set. Offer the choice, don't
default them on, and announce the cost — ask "Want a stability check — would the
same features get picked if the data were resampled? It re-runs selection many times
so it takes a while; a deeper version also re-builds the features each time, costing
more again." and run only what the user opts into. When neither is requested, add
neither and set Results.StabilitySkipped = true (report says "not requested"). Skip
a requested gate automatically with that flag when the pool has < 2 features or < 2
subsamples are possible.
5. Deliver — references/deliver.md
Write the artifacts to the confirmed output directory:
[ScriptPath, MatPath] = writeInferenceScript(Recipe, SelectedNames, OutputDir, ...
DatasetName = DatasetName, ResponseVar = ResponseVar);
SelectedTbl = selectFeatures(Recipe, RawTbl, SelectedNames); % in-workspace CHECK only — do NOT write to diskThe recipe is the inference recipe — fe_transform_<dataset>.m +
its companion .mat reproduce the exact selected set on new raw data with one
call. writeInferenceScript is generator-agnostic (SMLT FeatureTransformer or a
Kind=="domain" recipe) and inlines its logic, so the deliverable stands alone.
SelectedTbl is only a verification value — never write the feature table to
disk (features_<dataset>.mat/.csv); the inference script regenerates it on
demand, so a materialized dump is redundant. Write the figures' fig_*.svg
alongside only when the report is on; under a report opt-out the inference
script + state are the whole deliverable.
Also save the full Results struct unconditionally — every run result at full
resolution, so nothing a plot or a truncated table only previews is lost:
ResultsMatPath = saveResults(Results, OutputDir, DatasetName = DatasetName);This fe_results_<dataset>.mat ships even on a report opt-out (assemble Results
first — see report.md). Tell the user it exists, and that
the same data is live in the Results workspace variable this session.
6. Report — references/report.md
Optional (opt-out, default on) — run only if the user kept the report at intake
(GenerateReport == true), the same flag that gates every figure in
Select/Assess/Deliver. When run, assemble the plain Results struct from every
prior phase and emit:
MdPath = generateFeatureReport(Results, OutputDir); % always
if hasReportGenerator(); PdfPath = generateFeatureReportPdf(Results, OutputDir); end % if available7. Hand off to model training (offer, don't invoke)
This skill stops at features. If the user's ask mentioned training a model (supervised learning, classification, regression, etc.) and an installed skill covers tabular model training, name that candidate once at close-out and offer to hand off — semantic match against the injected skill list, confirm before invoking, no hardcoded skill names (same discovery rule as domain-routing.md). If nothing fits or the ask was features-only, stop silently.
Behavioral contract (all phases)
Train/test discipline — generation, selection, and all training-side statistics use the training rows only; the test rows certify the delivered artifact and are never seen before then.
Generator-agnostic downstream — after the pool phase, touch generator internals only through
describeFeatures/transformFeatures.No hidden truncation — if you cap, sample, or skip anything, say so.
Never fabricate a pool — when routing offers a choice, or the data needs a tabularizing step this skill can't perform, presenting the choice or asking ends the turn. Wait for the user before building the pool by any means (domain skill, generic path, or an extractor of your own).
Confirm outward-facing actions — writing to disk is confirmed once at intake (the output-dir hard-halt); don't write outside it.
MathWorks Coding Guidelines for any MATLAB you author (lowerCamelCase,
argumentsblocks, ≤6 in / ≤4 out,endterminators; modern APIs).
MATLAB and Simulink are registered trademarks of The MathWorks, Inc. See www.mathworks.com/trademarks for a list of additional trademarks.
Copyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.1 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md18.9 KiB
- manifest.yaml570 B
- references/assess.md16.7 KiB
- references/deliver.md13.0 KiB
- references/domain-routing.md18.1 KiB
- references/feature-pool.md10.9 KiB
- references/intake.md16.9 KiB
- references/internal-helpers.md12.2 KiB
- references/provider-protocol.md12.3 KiB
- references/report.md9.7 KiB
- references/reproduce-pool.md7.1 KiB
- references/select.md7.1 KiB
- scripts/BordaVoter.m2.3 KiB
- scripts/ConsensusVoter.m2.5 KiB
- scripts/MajorityVoter.m2.0 KiB
- scripts/RRFVoter.m1.9 KiB
- scripts/assessGenerationStability.m11.9 KiB
- scripts/assessKFold.m20.6 KiB
- scripts/assessSelectionStability.m9.8 KiB
- scripts/baselineComparison.m18.9 KiB
- scripts/buildRecipe.m8.2 KiB
- scripts/chooseNumBins.m5.0 KiB
- scripts/chooseStandardization.m4.5 KiB
- scripts/computeNSelect.m2.9 KiB
- scripts/createConsensusVoter.m2.2 KiB
- scripts/describeFeatures.m5.1 KiB
- scripts/expandBlockMap.p(opens on GitHub)Not mirrored: opens on GitHub440 B
- scripts/featureSetQuality.m12.3 KiB
- scripts/generateFeatureReport.m67.2 KiB
- scripts/generateFeatureReportPdf.m64.9 KiB
- scripts/generateFeatures.m14.6 KiB
- scripts/hasReportGenerator.m630 B
- scripts/isDomainRecipe.m1.4 KiB
- scripts/isStringEmpty.m577 B
- scripts/lambdaSequence.p(opens on GitHub)Not mirrored: opens on GitHub637 B
- scripts/lassoFeatureRanking.p(opens on GitHub)Not mirrored: opens on GitHub971 B
- scripts/macroF1Score.m931 B
- scripts/mrmrFeatureRanking.p(opens on GitHub)Not mirrored: opens on GitHub239 B
- scripts/mutualInformationPanel.p(opens on GitHub)Not mirrored: opens on GitHub1.1 KiB
- scripts/ncaFeatureRanking.p(opens on GitHub)Not mirrored: opens on GitHub464 B
- scripts/nogueiraStability.m3.8 KiB
- scripts/oobFeatureRanking.p(opens on GitHub)Not mirrored: opens on GitHub414 B
- scripts/plotSelectionConsensus.m2.7 KiB
- scripts/plotSelectionDecision.m6.3 KiB
- scripts/plotSelectionStability.m10.2 KiB
- scripts/plotValidationBaseline.m6.1 KiB
- scripts/printSummary.m18.3 KiB
- scripts/profileForSplit.m6.2 KiB
- scripts/rankAgreement.m1.3 KiB
- scripts/rankFeatures.m6.2 KiB
- scripts/reserveHoldoutForUser.m5.5 KiB
- scripts/runConsensusSelection.m9.4 KiB
- scripts/sampleSkewness.m600 B
- scripts/saveResults.m2.1 KiB
- scripts/scoreFeaturePanel.p(opens on GitHub)Not mirrored: opens on GitHub1.6 KiB
- scripts/screenPredictors.m13.3 KiB
- scripts/selectFeatures.m2.3 KiB
- scripts/splitStrategy.m5.4 KiB
- scripts/stratifiedDownsampleRows.m2.2 KiB
- scripts/stratifiedSubsampleIndices.m3.4 KiB
- scripts/transformFeatures.m6.1 KiB
- scripts/validateRecipe.m12.3 KiB
- scripts/writeInferenceScript.m10.5 KiB
references/domain-routing.md
Domain Routing — hand pool-building to a domain skill
Some data belongs to a specialized domain where purpose-built extraction yields
far better features than generic gencfeatures could — whether the data arrives
in a non-tabular modality or as a domain-specific table. When an installed skill
covers that domain, route the feature-pool phase to it and
keep everything downstream unchanged.
This file defines how that routing decision is made and executed.
How discovery actually works
Discovery is matching against the skill list already in context — not a filesystem scan, not a registry file.
The harness injects the installed-skills list (each skill's name + description) into the conversation. That list is the source of truth. "Discovering" a domain skill means semantically matching the user's data against those description lines — nothing is read from disk, and no registry is maintained.
Do not walk ~/.claude, glob for SKILL.md files, or hard-code a table of
known domain skills. Skills come and go with what the user has installed; the
injected list already reflects that. Hard-coding names would rot.
The routing decision
Run this at the start of the feature-pool phase.
1. Decide whether a domain skill applies
This skill is tabular-only. Every phase — pool contract, selection,
assessment, delivery — operates on a table of predictors + response, and the pool
always arrives as a table. The only question this step answers is who should
produce that table: the generic generateFeatures (gencfeatures) path, or a
domain-specific extraction skill.
The deciding factor is whether a domain skill's domain fits the data — not how the data is stored. Storage shape and domain applicability are independent:
Data stored as a table does not rule out a domain skill. Raw domain measurements are often stored tabularly yet still call for domain-specific extraction — a domain skill will produce far better-justified features than generic
gencfeatureswould, even though the input was a.csvortable.Data in a non-tabular modality (a signal vector, a
timetable, an ensemble datastore) always needs a domain skill to become a feature table, becausegencfeaturescannot consume it at all.
So consider a domain skill whenever the data's subject matter matches an
installed domain extractor, regardless of its storage shape. Use the generic
generateFeatures path when no domain skill fits the domain — that is the
default for ordinary predictor tables with no specialized structure.
2. Match against the injected skill list
Read the user's data modality + domain description (collected at intake) and compare it to the name+description of each installed skill. A skill is a candidate when its description clearly covers this data's domain and modality — that is, when the data's subject matter falls squarely inside what the skill advertises it extracts features for.
Judge by the description text, not by a remembered list. If two skills plausibly match, prefer the one whose When to Use / When NOT to Use boundaries fit the data most precisely.
3. Confirm before invoking
Never invoke a domain skill silently. State the match, name the candidate skill, give the reason, and present the choice as two named paths — the domain skill (recommended default) or this skill's own generic feature generation — so the user sees the alternative explicitly rather than only being able to decline. Follow this template (fill the bracketed parts from the actual match):
Your data looks like [domain / modality]. Two ways to build the feature pool:
[
skill-name] (recommended) — purpose-built for this domain; it produces the features, then I run consensus selection and assessment on them.Generic generation — this skill's own
gencfeatures-based path, no domain skill.I'd suggest [
skill-name]. Shall I use it, take the generic path, or point me at a different skill (or supply the features yourself)?
Proceed to the domain skill only on an affirmative. Choosing the generic path is a first-class option, not a fallback-on-refusal — take it to §6's tabular default. This is Open Item O1's confirm-before-invoke rule.
Presenting the choice ends your turn — stop and wait. Building the pool is a later turn gated on the user's reply, never a formality you satisfy and then blow past. Until they answer, do not build the pool by any means — not the domain skill, not the generic path, and not an extractor of your own.
4. Invoke the domain skill — and resume on its output
On confirmation, run the chosen skill in the same stateful MATLAB session — that
is what makes the join back data, not control flow: the domain skill leaves its
output in the workspace, and you resume on it. There are two paths for where
that run happens, a main path and a fallback. They differ only in J1 (where the
skill runs); both leave the identical workspace artifacts and both converge on
buildRecipe (§5), so everything downstream is unchanged either way.
Pre-condition — the split is already decided (non-negotiable, prevents leakage).
Before the domain skill runs, intake's Collect step must be finished and the
train/test split established over the raw observations (problem type +
splitStrategy on the response, which needs no features). The split — which
observations are train vs. test — is then handed to the domain skill as data
so any extraction it fits trains on training observations only. Do not invoke
the domain extractor before this holds: extracting first and splitting later fits
the extraction on all rows and leaks test information into the pool (the failure
mode intake.md's ordering invariant guards against).
Whichever path runs, the domain skill's run must leave two post-conditions in the workspace, and you own assembling them (§5):
A feature table with the split already applied — one row per observation, predictors + response, a named workspace variable, train/test membership carried through so the same rows stay train vs. test downstream. This is the pool. Any extraction that fits cross-row state (a normalization, a learned filter bank, an encoder, PCA) must have been fit on the training observations only; a purely stateless per-observation extractor is split-order indifferent, but declare which case applies rather than assuming.
A replayable extraction transform built via
buildRecipe(§5) — so the delivered inference script can reproduce those features on new raw data, the same guarantee the SMLT path gives.
Main path — spawn the domain skill in a subagent
Spawn the domain skill via the Agent tool. An Agent-tool subagent shares this
same stateful MATLAB MCP session (verified: workspace state, and even a live
Apply handle with captured fitted state, cross the boundary both ways), so the
data-join is unchanged — the subagent leaves the pool and the recipe in the shared
workspace exactly as inline execution would. What the subagent adds is a clean
contract boundary: its deliberation (parameter questions, method choices,
reference reads) stays in its own context instead of the orchestrator's, and its
final message is a structured return manifest — the arguments buildRecipe
needs (the pool variable name, Apply, Describe, Provider) plus the
stateless-vs-fitted declaration from post-condition 1.
Give the subagent, in its prompt: the skill to run, the split as data (the
train row indices), a reserved workspace namespace for its outputs
(domainHandoff_*), and the required manifest schema. On return, assemble the
recipe with buildRecipe (§5); if the manifest is incomplete or malformed,
re-request the missing piece from the same subagent (resume it by its
agentId — its context is preserved) up to twice before treating the handoff
as failed.
Fallback path — inline, same conversation
Run the chosen skill through the Skill tool in this same conversation: the same agent drives the extraction via MCP and assembles the recipe from the commands it observed itself run. This is the original mechanism; it is fully capable of the whole job, and is the path to take when the subagent path is mechanically unavailable:
| Trigger | Response |
|---|---|
| Subagent will not spawn, or dies on a terminal error | clear the domainHandoff_* slots, run inline |
| Manifest still invalid after two re-requests | clear the domainHandoff_* slots, run inline |
Round-trip verification fails (Apply won't replay) | not a path switch — handle via two-tier delivery (§5) |
The last row matters: a recipe that fails buildRecipe's round-trip gate is a
capture-fidelity failure, and inline reconstruction is the weaker capture, so
falling back would not help — it is handled by the honest two-tier delivery in §5,
under either path. Clearing the reserved slots before an inline retry matters
because a half-finished subagent may have left partial artifacts in the shared
workspace.
Skill unavailable or unlicensed — inline won't help, so don't try it. The inline fallback above assumes the skill can run and only the subagent mechanism failed. Two failures break that assumption: the chosen skill cannot be loaded at all (not installed, missing files), or it loads but a required toolbox license is unavailable. Inline runs the same skill against the same license, so it can't rescue either. Instead report the specific issue to the user (name the skill and whether it was a load failure or a license one), then branch on data shape — the same split §6 draws:
Data is already a usable predictor table — offer the generic SMLT path (
generateFeatures) as the fallback, framed as a genuine choice: "[skill] is [unavailable / unlicensed], so I can't build domain features. Your data is already tabular, so I can run generic feature generation instead — want that, or would you rather fix the skill/license first?" Proceed to §6's tabular default on an affirmative.Data is a non-tabular modality — generic generation cannot consume it and this skill does not tabularize raw modalities, so there is nothing to fall back to. Halt cleanly, stating the reason and what would unblock it (install/license the skill, or supply an already-extracted feature table and re-enter intake). Do not coerce the raw modality into
gencfeatures.
Resume waypoint (both paths). Once both post-conditions hold, you do not re-run
intake and you do not wait for a return — the intake context (response, output dir,
model family, dataset name) and the already-decided split are still in hand.
Continue directly at the select phase, running selection on the
pool's training rows, passing the captured pool in place of what
generateFeatures would have produced. Everything downstream (select → assess →
deliver → report) is unchanged.
One assess-time consequence — the baseline's "original" reference. When the
domain input was a non-tabular modality (a signal, image, timetable the
extractor consumed), there are no raw predictor columns a model could fit on, so
the baseline cannot measure lift over "raw inputs." In that case the assess phase
takes the full extracted pool as the "original" reference and reports the lift
of consensus selection over the whole pool instead — see assess.md §1
for the exact OriginalData/OriginalPredVars rule. A domain input that was
already a table of model-ready predictors keeps the ordinary raw-vs-selected
baseline.
5. Capture both artifacts — table and replayable transform
The feature table is only half of what the pipeline needs. The delivered artifact
is a self-contained inference script (fe_transform_<dataset>.m + .mat, see
deliver.md), and that can only be emitted if the extraction was
captured as something re-runnable. So capture two things from the domain
skill's run — you assemble both, from the commands it actually executed:
FeatureTable— the feature table the skill produced (predictors + response). This is the pool that goes into select.md.Recipe— a domain Recipe struct built withbuildRecipe(provider-protocol.md), carryingKind = "domain", anApplyhandle that replays the full extraction, aDescribetable (Feature | Type | Definition), and aProviderlabel. Downstream reads the pool only through this contract (see feature-pool.md), so it stays fully generator-agnostic —transformFeatures/describeFeatures/selectFeaturestreat a domain Recipe exactly like an SMLTFeatureTransformer.
Recipe.Apply is the replayable pipeline writeInferenceScript packages —
the domain-path equivalent of the SMLT FeatureTransformer. Two rules govern its
capture, and both are non-negotiable because they are what make inference correct:
Prefer the skill's own exporter. Many MathWorks skills can emit a self-contained extraction function of their own. If the chosen skill does, wrap that as
Applyrather than reconstructing from a transcript — it is the faithful source. Otherwise assembleApplyfrom the captured commands.Persist fitted state; fit it on the training rows only. If the extraction fits anything on the data (a normalization, a learned filter bank, an encoder), those parameters must be baked into
Apply/the.matand fit on the training rows only — never recomputed at inference (that drifts) and never fit on the full table (that leaks). The SMLT path gets this for free becausegenerateFeaturesreceivesTrainTbl; on the domain path you must carry the train/test discipline into the domain skill's run explicitly.
Build and verify the recipe in one call. Hand the captured pieces to
buildRecipe — it assembles the struct and runs the round-trip gate inline, so a
recipe that is missing a field or cannot reproduce its pool fails here rather than
downstream:
Recipe = buildRecipe(Apply, Describe, Provider, FeatureTable, RawInput, ...
ResponseVar=ResponseVar, CheckIdx=TestIdx);Pass TestIdx so response-based encodings don't false-alarm on training rows.
buildRecipe errors with buildRecipe:invalidProvider /
validateRecipe:missingArtifact / :poolMismatch / :describeMismatch.
ApplySelected is a deliver-time artifact, validated separately then (see
deliver.md); buildRecipe neither builds nor requires it.
Two-tier delivery when capture can't be verified. Capture is best-effort. If
buildRecipecannot produce a verified recipe — an irreducibly interactive or human-judgment extraction step that isn't a pure function of a raw table, or a round-trip gate that keeps failing — do not switch routing paths (the inline fallback is the weaker capture, so it won't help) and never fabricate a clean-looking transform that doesn't replay. Instead deliver two tiers: always ship selection + assessment + the selected columns from the trustworthy feature table, and ship the full self-contained inference script only when the recipe verifies — otherwise state the honest fallback, "re-run extraction via[skill-name], then apply the selected-column subset."
6. When routing doesn't happen
Routing to a domain skill can be declined or find no match. (A domain skill that was chosen but whose subagent run failed mechanically does not land here — that degrades to the inline path in §4, not to generic generation.) What to do when no domain skill runs at all depends only on whether the data is already a usable predictor table:
Data is already a usable predictor table (one row per observation, feature columns + response) — use the generic
generateFeatures(gencfeatures/genrfeatures) path. This is simply the default tabular path; it needs no confirmation. This covers ordinary tables that matched no domain skill, and domain-shaped tables where the user preferred the generic path.Data is a non-tabular modality (a signal vector,
timetable, ensemble datastore, …) —gencfeaturescannot consume it, and this skill does not tabularize raw modalities itself. This is not a refusal of the workflow, it is a sequencing requirement: a tabularizing step must run first. Pause and ask the user to either supply an already-extracted feature table, or point to a skill/step that covers the domain — then re-enter intake with the resulting table and run the normal tabular path. This skill does not tabularize raw modalities by any means — neither by coercing the generic path nor by writing your own extractor; both fabricate a pool the user never sanctioned. Pausing ends your turn: wait for a table or a named skill/step before going on.
7. Record the route
Discovery is an LLM semantic match (§2) and cannot be made deterministic while it is description-based — but the decision must be reproducible after the fact. Record in the run manifest / report: the domain-vs-generic choice; if domain, the chosen skill's name and version (the version as advertised in the injected skill list — record "unversioned" if it carries none); which path ran (subagent or inline fallback); and, if it fell back, why. This does not make the first decision deterministic; it makes a re-run pinnable to the same route and lets a reviewer see which extractor produced the pool.
What this file deliberately does not do
No registry. The injected skill list is the registry.
No filesystem discovery. Nothing under
~/.claudeis read at run time.No silent invocation. Confirm-before-invoke, always.
No re-engineering domain features. The domain skill's table is the pool as- is;
gencfeaturesis never layered on top of it.
MATLAB and Simulink are registered trademarks of The MathWorks, Inc. See www.mathworks.com/trademarks for a list of additional trademarks.
Copyright 2026 The MathWorks, Inc.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/ai-and-statistics/matlab-engineer-tabular-features · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.
Get it from the source
8 of this skill’s files are not mirrored here, so there is no ZIP. Get the complete skill from its source: matlab/matlab-agentic-toolkit