This document describes the current SFT adapter at tasks/sft/src/index.ts.
The SFT task is implemented using the createTaskAdapter factory:
export const sftAdapter = createTaskAdapter({
manifest: { taskId: "sft", ... },
run: runSftTask,
terminate: async () => { sftEnvironment.cleanup(); },
});run(context): Parses configuration, runs optional QUEST+ staircase calibration, then executes main trials viaTaskOrchestrator.terminate(): CallssftEnvironment.cleanup()(aTaskEnvironmentGuard) to reset the cursor and remove keyboard scroll blockers.
SFT currently runs jspsych via the LifecycleManager.
Top-level sections consumed:
taskinstructionsdesigntimingdisplayresponsesstimulusstaircasefeedback
titleinstructions(legacy intro fallback)
Note:
- SFT now uses shared instruction-slot parsing from
instructions:- intro:
pages|introPages|intro|screens - pre-block:
preBlockPages|beforeBlockPages|beforeBlockScreens - post-block:
postBlockPages|afterBlockPages|afterBlockScreens - end:
endPages|outroPages|end|outro
- intro:
task.instructionsremains a fallback default intro page.- SFT now supports
instructions.blockSummary(core-level) for block-end computed summary cards. - SFT also supports:
instructions.blockIntroTemplate(default"Rule: {blockRule}. Trials: {nTrials}.")instructions.showBlockLabel(defaulttrue)instructions.preBlockBeforeBlockIntro(defaultfalse)
Required:
design.manipulations[]design.blocks[]
Manipulation parsing supports:
idrule(OR|AND|XOR|ID|MIXED)- optional
trialPlan.variants[]ortrial_plan.variants[] - optional
trialPlan.scheduleortrial_plan.schedule
MIXED without explicit variants expands into OR/AND/XOR variants.
Block parsing supports:
idorblock_idlabelnTrialsorn_trialsmanipulation(single id)manipulations(must resolve to exactly one final id for SFT)manipulationPool(draws fromdesign.manipulationPools)feedback(per-block feedback override)beforeBlockScreens(optional string or string[]; extra continue screens before block trials)afterBlockScreens(optional string or string[]; extra continue screens after block-end summary)
Legacy aliases still accepted:
preBlockInstructions->beforeBlockScreenspostBlockInstructions->afterBlockScreens
Optional:
design.manipulationPools:poolId: [ ["manipA"], ["manipB"], ... ]or bundles- each block using
manipulationPooldraws participant-seeded without replacement
Variant-level trial plan fields:
trialPlan/trial_plan.variants[].idtrialPlan/trial_plan.variants[].ruletrialPlan/trial_plan.variants[].weighttrialPlan/trial_plan.variants[].trial_pooltrialPlan/trial_plan.variants[].trial_pool_scheduletrialPlan/trial_plan.variants[].layouttrialPlan/trial_plan.variants[].show_rule_cuetrialPlan/trial_plan.variants[].rule_cue_label
Schedule modes (trial_plan.schedule and trial_pool_schedule) are powered by core buildScheduledItems:
weighted(default)sequencequota_shuffle(and aliasblock_quota_shuffle)- optional
withoutReplacement/without_replacement
timing.fixation_truncexp.mean(default500)timing.blank_ms(default66)timing.stimulus_ms(default100)timing.response_deadline_ms(default3000)timing.response_terminates_trial(defaulttrue)
aperture_px(default250)dot_offset_px(default44)dot_radius_px(default7)canvas_background(default#000000)canvas_border(default2px solid #444)cue_color(default#0f172a)dot_positions_mode(fallback layout source for variants when unspecified)
Key mappings:
keys.OR.yes,keys.OR.nokeys.AND.yes,keys.AND.nokeys.XOR.yes,keys.XOR.nokeys.ID.AB,keys.ID.AN,keys.ID.NB,keys.ID.NN
salience_levels.high/lowcondition_codes[]
If enabled:
- runs QUEST+ calibration phase
- updates global low/high salience for subsequent main trials
Parsed fields:
enabledn_trialsstim_db_min,stim_db_max,stim_db_stepslope_samples,lapse_samples,guess_ratelow_scale,high_scaleclamp_luminance
SFT uses the shared core feedback module.
Global and per-block feedback config supports:
enableddurationMs/duration_msmessages.correct|incorrect|timeout|invalidmessages.byResponseCategory/messages.by_response_categorystyle.correctColor|incorrectColor|timeoutColor|invalidColor(snake_case accepted)style.byResponseCategoryColors/style.by_response_category_colorsstyle.fontSizePx|fontWeight|canvasBackground|canvasBorder(snake_case accepted)
Each trial timeline can include:
- fixation
- blank
- response window (jsPsych keyboard response)
- optional post-stimulus hold phase
Key handling:
- allowed keys are mapped with
toJsPsychChoices - jsPsych responses are normalized with
normalizeKey
Main trials are scored through evaluateTrialOutcome.
SFT session finalization is handled internally by TaskOrchestrator (via the core data sink and JATOS submission pipeline). Task adapters do not call finalizeTaskRun directly.
CSV export uses the suffix sft_trials.
The session payload includes:
records: main-trial rows with participant/block/trial metadata,rule,layout,stimCode, channels, stimulus category, expected/observed response categories and keys, RT and correctness.staircaseRecords: staircase-phase calibration rows (when staircase is enabled).events: lifecycle event log (task_start,block_start,block_end,task_end).
{
"design": {
"manipulations": [
{
"id": "logic_mix",
"trial_plan": {
"schedule": { "mode": "quota_shuffle" },
"variants": [
{
"id": "or_dense",
"rule": "OR",
"weight": 2,
"trial_pool": ["HH", "Hx", "xH"],
"trial_pool_schedule": { "mode": "quota_shuffle" }
},
{
"id": "and_sparse",
"rule": "AND",
"weight": 1,
"trial_pool": ["LL", "Lx", "xL"],
"trial_pool_schedule": { "mode": "sequence", "sequence": ["LL", "Lx", "xL"] }
}
]
}
}
],
"blocks": [
{ "id": "B1", "label": "Mix 1", "nTrials": 24, "manipulation": "logic_mix" },
{ "id": "B2", "label": "Mix 2", "nTrials": 24, "manipulation": "logic_mix" }
]
}
}{
"design": {
"manipulations": [
{ "id": "or_only", "rule": "OR" },
{ "id": "xor_only", "rule": "XOR" }
],
"manipulationPools": {
"alt_or_xor": [["or_only"], ["xor_only"]]
},
"blocks": [
{ "id": "B1", "nTrials": 20, "manipulationPool": "alt_or_xor" },
{ "id": "B2", "nTrials": 20, "manipulationPool": "alt_or_xor" }
]
}
}Notes:
- SFT currently requires each block to resolve to exactly one manipulation id.
- Use
trial_plan.variantsfor within-block trial-type mixing, andmanipulationPoolfor across-block assignment.