Silverstripe Version Upgrade Skill
Repeatable workflow for upgrading Silverstripe CMS projects (e.g. SS4 → SS5) in Dynamic Agency's module ecosystem and DDEV-based local development.
Scope: This skill covers SS4 and later major-version bumps (SS4 → SS5, SS5 → SS6) — a recurring, recipe- and dependency-driven upgrade. For a legacy SS3 → SS4 upgrade, use the silverstripe-3-to-4-upgrade skill instead: that is a one-time structural migration (PSR-4 namespacing,
mysite→app,public/directory, DB schema preflight, Blocks→Elemental) with little mechanical overlap with this workflow.
Upgrade Phases (Summary)
| Phase | Key Actions |
|---|---|
| 1. Assessment | Audit versions, packages, incompatibilities; clone legacy instance for VR baseline |
| 2. Branch & PHP | Create feature branch, update PHP to ^8.1 |
| 3. Dependencies | Update recipes with --no-update, resolve, vendor-expose |
| 4. Config Migration | Fix extension relocations, ORM relationships, templates |
| 5. Data Migration | Write idempotent BuildTasks for link/data migrations |
| 6. Build & Verify | dev/build, run tasks, check frontend + CMS |
| 7. Code Quality | PHPCS, PHPStan, annotation fixes |
| 8. Commit & PR | Conventional commit, checklist PR body |
| 9. UAT | Deploy, run tasks, verify, troubleshoot |
| 10. Production | Merge, deploy, run tasks, verify |
Do not rationalize
Every phase gate below requires evidence: the actual command output, not a recollection or an inference. "Seems right" is insufficient. The shortcuts agents talk themselves into, and the required counter-behavior:
| Rationalization | Required behavior |
|---|---|
| "The build passed, so the migration ran" | Run each migration task and paste its output, including row counts. dev/build succeeding proves nothing about task execution. |
| "The vendor diff looks fine" | Run ddev composer validate and ddev composer show --direct, paste the output. Eyeballing composer.lock is not validation. |
| "Tests probably still pass" | Run the suite (ddev exec vendor/bin/phpunit) and paste the summary line. No run, no claim. |
| "The homepage loads, so the site works" | Curl one URL per page type and paste the HTTP codes (see Phase 6 and 10.4). One page proves one template. |
| "dev/build ran without errors, so the schema is correct" | Paste the build output showing the table and field changes you expected. A silent build can mean silently ignored config (see classname_value_remapping). |
| "That warning is probably pre-existing" | Grep the pre-upgrade branch for the same warning and paste both results. Confirmed pre-existing or it is yours. |
Phase 1: Assessment & Discovery
1.1 Pre-Flight
ddev start
git branch -a | grep -i 'ss5\|silverstripe-5\|upgrade'
ddev composer show silverstripe/framework | grep versions
ddev php -v
# SS5 requires PHP 8.1+; SS6 requires PHP 8.3+ — update .ddev/config.yaml if needed
1.2 Audit Packages
ddev composer show | grep 'dynamic/\|silverstripe/\|dnadesign/'
1.3 Incompatible Packages (Common)
| Package | Action |
|---|---|
lekoala/silverstripe-debugbar |
Remove |
ryanpotter/silverstripe-cms-theme |
Remove (SS5 built-in) |
fractas/elemental-stylings |
Remove (use native styles) |
sheadawson/silverstripe-linkable |
Replace with silverstripe/linkfield |
dynamic/silverstripe-company-config |
Remove (merged into base-site) |
dynamic/silverstripe-template-config |
Remove (merged into base-site) |
SS6-specific
| Package | Action |
|---|---|
nathancox/embedfield |
Replace with fromholdio/silverstripe-embedfield ^5.1 |
[!TIP] If the project uses
sheadawson/silverstripe-blocks, migrate to Elemental before SS5 upgrade usingdynamic/silverstripe-blocks-to-elemental-migrator.
1.4 Legacy instance (for VR baseline)
Clone the current SS4 site as a side-by-side reference before starting the upgrade. This gives you a pixel-perfect local baseline to diff against during VR verification — same approach as the SS3→SS4 upgrade pattern.
Naming convention: clone into ~/Sites/{project}-legacy so the upgrade lives in ~/Sites/{project}.
cd ~/Sites
git clone <repo> {project}-legacy
cd {project}-legacy
git checkout main # or the current production branch
ddev config --project-name {project}-legacy
ddev start
ddev auth ssh
ddev exec ./sync.sh # sync prod DB and assets
This gives you https://{project}-legacy.ddev.site — a running SS4 instance against the same data the upgrade will use. In the Build & Verify phase you'll diff this against the SS5 upgrade to confirm visual parity.
Phase 2–3: Branch & Dependencies
git checkout -b feature/silverstripe-5-upgrade
# Update PHP requirement in composer.json
# Change "php": "^7.4" to "php": "^8.1" (SS5 requires PHP 8.1+)
# Update core
ddev composer require silverstripe/recipe-cms:^5 dynamic/recipe-silverstripe-base-site:^5 --no-update
# Update elemental packages (versions vary per package — verify on Packagist)
# Add each package individually, e.g.:
ddev composer require dynamic/silverstripe-elemental-accordion:^5.0 --no-update
# Remove incompatible
ddev composer remove lekoala/silverstripe-debugbar --no-update
# Resolve
ddev composer update --with-all-dependencies
ddev composer vendor-expose
SS6 Variant
For SS6 upgrades, adapt the branch and dependency steps:
git checkout -b feature/silverstripe-6-upgrade
# Verify PHP version — SS6 requires PHP 8.3+
ddev php -v | grep -oP 'PHP \K[0-9]+\.[0-9]+'
# Must be 8.3 or higher — update .ddev/config.yaml if needed
# Change "php": "^8.1" to "php": "^8.3" in composer.json
# Update core recipe
ddev composer require silverstripe/recipe-cms:^3 dynamic/recipe-silverstripe-base-site:^8 --no-update
# Update elemental packages (branch 6 for most elemental modules)
ddev composer require dynamic/silverstripe-elemental-accordion:^6.0 --no-update
# Add SS6-required packages that SS5 had as transitive
ddev composer require silverstripe/htmleditor-tinymce:^1.0 --no-update
# Replace deprecated packages (linkfield 4.x is CMS 5 only; the SS6 line is 5.x.
# Run the linkable data migration on SS5 with linkfield ^4 BEFORE this bump)
ddev composer require --no-update silverstripe/linkfield:^5.0
ddev composer remove --no-update sheadawson/silverstripe-linkable
# Remove incompatible
ddev composer remove --no-update nathancox/embedfield
# Resolve
ddev composer update --with-all-dependencies
ddev composer vendor-expose
Recipe-first: When consuming the Dynamic Essentials ecosystem, require only the root recipe:
ddev composer require dynamic/recipe-silverstripe-essentials-website:^3@devThe recipe's@devconstraints cascade the entire tree automatically. Individual module constraints are only needed if forcing source install viapreferred-install: dynamic/*: source.
silverstripe/vendor-pluginbump: SS6 Dynamic modules require^3. If the project root pins^2.0(SS5 default), Composer will conflict. Apply before runningcomposer update:ddev composer require silverstripe/vendor-plugin:^3 --no-update
[!WARNING] SS6 blocker —
silverstripeltd/betamask:silverstripeltd/betamask ^0.0.1requiressilverstripe/admin ^2.1(SS5-only). Check for an SS6-compatible release before removing it:ddev composer show -a silverstripeltd/betamask # check for an SS6-compatible release ddev composer remove silverstripeltd/betamask # if none found
Evidence gate (Phase 3): before moving to config migration, paste the output of:
ddev composer validate
ddev composer show --direct # confirm expected major versions resolved
A clean composer update exit code is not the gate; the resolved version list is.
Phase 4: Configuration Migration
Key areas:
- Extension relocations:
HeaderImageExtensionmoved from base-site → site-tools - ORM strictness: SS5 requires explicit
has_oneback-references forhas_many - Template updates: Sort field
SortOrder→Sort, link$Link→$URL - Annotations: Use
@property ?stringfor nullable fields in PHP 8.1+
SS6 note: BuildTask signature changed (
run($request)→execute(InputInterface, PolyOutput): int). See references/data-migration-tasks.md for the SS6 Symfony Console Command template.
SS6 Configuration Migration
Namespace and class renames
SS6 relocated several core framework classes. Every use statement referencing an old FQCN causes a PHP fatal error at runtime (often first surfacing on dev/build). Grep for each old name and replace:
| Old (SS5) | New (SS6) |
|---|---|
SilverStripe\View\ViewableData |
SilverStripe\Model\ModelData |
SilverStripe\View\ArrayData |
SilverStripe\Model\ArrayData |
SilverStripe\ORM\ArrayList |
SilverStripe\Model\List\ArrayList |
SilverStripe\ORM\ValidationResult |
SilverStripe\Core\Validation\ValidationResult |
SilverStripe\ORM\ValidationException |
SilverStripe\Core\Validation\ValidationException |
# Run each grep across app code, module code, and theme code
rg "SilverStripe\\\\View\\\\ViewableData" app/src/ src/ themes/*/code/
rg "SilverStripe\\\\View\\\\ArrayData" app/src/ src/ themes/*/code/
rg "SilverStripe\\\\ORM\\\\ArrayList" app/src/ src/ themes/*/code/
rg "SilverStripe\\\\ORM\\\\ValidationResult" app/src/ src/ themes/*/code/
rg "SilverStripe\\\\ORM\\\\ValidationException" app/src/ src/ themes/*/code/
The wernerkrauss/silverstripe-rector SS6 level set automates most of these renames (see references/code-quality.md), but always run the greps afterward: string references in config YAML, Injector definitions, and docblocks are not rewritten by Rector.
Typed signature requirements
Two related changes cause PHP declaration-compatibility fatals if missed:
validate()overrides must declare the return type. AnyDataObjectsubclass overridingvalidate()must usepublic function validate(): ValidationResult(with the newSilverStripe\Core\Validation\ValidationResultimport). PHP throws a fatal declaration-compatibility error otherwise.ModelDatamagic-method overrides must match parent signatures. Subclasses ofModelData(formerlyViewableData) that override__get,__set,__isset,hasField,getField,setField, etc. must add the typed parameters and return types the SS6 parent declares.
# Find validate() overrides missing the return type
rg "public function validate\(\)(?!\s*:)" app/src/ src/ --pcre2
# Find ModelData subclasses, then inspect their overridden magic methods
rg "extends ModelData|extends ViewableData" app/src/ src/
BuildTask signature change
SS6 changes run($request) → execute(InputInterface, PolyOutput): int returning Command::SUCCESS.
Key differences:
| SS4/SS5 | SS6 |
|---|---|
protected $title = '...' |
protected string $title = '...' |
protected $description = '...' |
protected static string $description = '...' |
private static $segment = '...' |
protected static string $commandName = '...' (required) |
public function run($request) |
protected function execute(InputInterface $input, PolyOutput $output): int |
echo / $this->log() |
$output->writeln() |
| Implicit return | return Command::SUCCESS |
forTemplate() return type enforcement
SS6 enforces : string return type on forTemplate(). Never return false — return `` (empty string) instead.
// SS5 (still works but triggers deprecation)
public function forTemplate() { return false; }
// SS6
public function forTemplate(): string { return ''; }
BaseElement::getDescription() removed
SS6 Elemental removed BaseElement::getDescription(). Use private static string $class_description instead:
// SS5
public function getDescription() { return "My element"; }
// SS6
private static string $class_description = "My element";
DDEV database socket config
SS6 defaults to MySQL unix socket connections. DDEV uses TCP, so add to .ddev/config.yaml:
web_environment:
- SS_DATABASE_SERVER=db
Without this, dev/build fails with "Connection refused."
TinyMCE extraction
SS6 extracted TinyMCE from silverstripe/admin into silverstripe/htmleditor-tinymce ^1.0. If missing, the CMS Content field silently degrades to a plain textarea (data-editor="textarea" instead of data-editor="tinyMCE").
[!NOTE] Essentials recipe exception: in Essentials SS6 recipe projects the package arrives transitively through the recipe, and an explicit require causes conflicts — check
composer why silverstripe/htmleditor-tinymcebefore adding it (see thesilverstripe-essentials-websiteskill).
Fix: Add to composer.json under require (not require-dev):
"silverstripe/htmleditor-tinymce": "^1.0"
Diagnostic:
document.querySelector('[data-editor]').dataset.editor
// Returns "textarea" instead of "tinyMCE" → missing package
Theme template API changes: Linkable → LinkField
When migrating from sheadawson/silverstripe-linkable to silverstripe/linkfield:
| Linkable (SS5) | Linkfield (SS6) | Notes |
|---|---|---|
$Link |
$URL |
getURL() not getLink() |
$LinkURL |
$URL |
Consistent — use $URL |
$OpenInNewWindow |
$OpenInNew |
Renamed attribute |
$MenuTitle |
$Title |
Different semantics — getMenuTitle() returns type label |
$Site (SocialLink) |
$SocialChannel |
Enum → varchar mapping |
$X.setStyle('classes') |
Explicit <a class="..."> |
Linkfield renders bare links |
<% loop $HasOneLink %> |
<% with $HasOneLink %> |
has_one is not iterable in SS6 |
$ElementLink.LinkURL |
$ElementLink.URL |
Namespace change on sub-properties |
SS6 silent config breakers
SeoExtension removal — Remove from SiteTree.extensions if using a third-party search provider:
SilverStripe\CMS\Model\SiteTree:
extensions:
- Dynamic\Base\Extension\SeoExtension # REMOVE — causes PHP worker hangs in SS6
PasswordValidator — SS6 defaults to EntropyPasswordValidator. Old RulesPasswordValidator config is silently ignored:
# SS6 format (NOT the old min_test_score / test_names pattern)
SilverStripe\Security\PasswordValidator:
password_strength: 3 # 0-4 scale
Session cookie defaults — SS6 sets SameSite=Strict, cookie_secure=true. All existing sessions invalidated on first deploy — expected and normal. Verify:
curl -sI https://site.example.com/ | grep -i 'set-cookie'
# Expected: PHPSESSID=...; path=/; secure; HttpOnly; SameSite=Strict
classname_value_remapping YAML key renamed — In SS4/5 the key was SilverStripe\ORM\DatabaseAdmin. In SS6 the class no longer exists; use SilverStripe\Dev\Command\DbBuild instead. Using the old key is silently ignored — dev/build runs without error but no remapping occurs, leaving legacy class names in the DB and causing "obsolete type" warnings in the CMS for every affected page.
# SS4/SS5 (WRONG in SS6 — silently ignored)
SilverStripe\ORM\DatabaseAdmin:
classname_value_remapping:
OldClass: NewClass
# SS6 (correct)
SilverStripe\Dev\Command\DbBuild:
classname_value_remapping:
OldClass: NewClass
After correcting the key, a single dev/build flush=1 migrates all affected rows in both SiteTree and SiteTree_Live. The build output will confirm: "Correcting obsolete ClassName values for N outdated types." Side effect: migrated pages gain a draft/live diff and show as MODIFIED — republish them afterward.
Phase 5: Data Migration
See references/data-migration-tasks.md for the BuildTask pattern, migration principles, and common scenarios (Linkable → Link, ManyMany → LinkField).
Evidence gate (Phase 5): a migration step is done when you can paste, for each task:
- The task's own output (migrated / skipped / broken counts).
- A row-count query against the target tables, compared to the source count:
SELECT COUNT(*) FROM <SourceTable>; -- before
SELECT COUNT(*) FROM <TargetTable>; -- after
SELECT COUNT(*) FROM <TargetTable>_Live; -- if versioned
SELECT COUNT(*) FROM <TargetTable>_Versions;
Counts must match, or every row of the delta must be accounted for (skipped classes, intentional exclusions). "The task finished without errors" is not the gate.
Phase 6: Build & Verify
ddev sake dev/build "flush=1"
ddev sake dev/tasks/<task-segment>
Verify: Homepage, carousel, navigation, footer, CMS admin, elemental blocks.
Evidence gate (Phase 6): paste the dev/build output (expected table/field changes present, no obsolete-type warnings you cannot explain) and the HTTP status of one URL per page type:
for u in / /<page-type-urls>; do
curl -s -o /dev/null -w "%{http_code} $u\n" "https://{project}.ddev.site$u"
done
Visual regression (optional but recommended)
Diff the SS5 upgrade against the legacy SS4 instance from Phase 1.4:
# Path to the installed skill; adjust for your agent's skills dir
VR=~/.claude/skills/visual-regression-upgrade
# Crawl the legacy site for a URL list
cd ~/Sites/{project}-legacy
python "$VR"/scripts/crawl_urls.py \
--url https://{project}-legacy.ddev.site --limit 30 --out paths.txt
# Capture + diff both environments
cd ~/Sites/{project}
python "$VR"/scripts/capture.py \
--prod https://{project}-legacy.ddev.site \
--local https://{project}.ddev.site \
--paths-file ../{project}-legacy/paths.txt \
--out ./vr-out
python "$VR"/scripts/diff_report.py \
--in ./vr-out --out ./vr-out/report
See the visual-regression-upgrade skill (from jsirish/workflow-skills, installed separately) for setup, auth, mask config, and report interpretation.
Key Discoveries & Gotchas
[!WARNING] Raw SQL Data Migrations: When migrating from deprecated or obsoleted modules (such as legacy
dynamic/silverstripe-blocks), the modern ORM will not map legacy database columns (like$dbproperties) because the field definitions have been removed from the class. You must rely on rawDB::query()calls to preserve and remap data (e.g., copying oldPageLinkIDfields directly into newLinkFieldinstances).
[!NOTE] Empty Layout Containers: During block to element mapping, migrating structural containers (like Accordions or Promo galleries) that have exactly 0 items within them is intentional. Preserving the empty wrapper maintains its hierarchical placement in the DOM so editors can populate it post-upgrade without losing context.
[!TIP] AJAX Lazy Loading Optimization: Pre-generating Image asset manipulations (e.g.,
ScaleWidthorFill) and employing AJAX lazy-loading on heavily populated pages (such as dense galleries) is required. Rendering 200+ raw Image objects in a single PHP payload causes hard pre-prod timeouts in SS5. Note: in SS5,PageController::init()will still fire on AJAX endpoints unless explicitly bypassed.
[!IMPORTANT] Data duplication / Subtitle: The unified templates in SS5 ElementContent might render both
TitleandSubTitledomains redundantly. SS4 layouts that abused DB columns to handle split headings will show duplicate lines post-migration unless the legacy entries are scrubbed.
[!NOTE] SS5
httpError(): ThehttpError()routine in SS5 throws anHTTPResponse_Exception. Following the call with a simplereturnaids static analysis flow and squashes PHP linting notices without impacting application state.
[!WARNING] Elemental Styles &
fractas/elemental-stylings: In SS4, thefractas/elemental-stylingsmodule prefixed the elemental style values withstyle-(e.g.,style-modal,style-blue). In SS5, native styles are used, andgetStyleVariant()returns the raw value (e.g.,modal). If templates or SCSS rely on thestyle-prefix, you must either update the templates/SCSS, update the values in the database, OR write a simpleupdateStyleVariantDataExtension to re-apply the prefix.
SS6 Breaking Changes
[!WARNING] Core classes renamed in SS6.
ViewableData→ModelData,ArrayDatamoved toSilverStripe\Model,ArrayList→SilverStripe\Model\List\ArrayList,ValidationResult→SilverStripe\Core\Validation\ValidationResult. Everyusestatement referencing an old FQCN is a fatal error at runtime. Systematic grep, one per rename: see Phase 4.Typed signatures enforced in SS6.
validate()overrides need: ValidationResult;ModelDatasubclasses overriding__get,hasField,__isset, etc. need typed parameters and return types matching the parent. Both are PHP declaration-compatibility fatals if missed. See Phase 4.BuildTask signature changed in SS6.
run($request)→execute(InputInterface $input, PolyOutput $output): intreturningCommand::SUCCESS. All custom BuildTask subclasses must be updated.TinyMCE extracted.
silverstripe/htmleditor-tinymce ^1.0must be inrequire(notrequire-dev). If missing, CMS Content fields silently degrade to plain textareas — no error, no console warning.
classname_value_remappingYAML key changed. SS6 renamedSilverStripe\ORM\DatabaseAdmin→SilverStripe\Dev\Command\DbBuild. The old key is silently ignored — remapping never runs, pages keep obsolete class names. Fix the YAML key and re-rundev/build flush=1.DB connection default changed. SS6 defaults to unix socket. DDEV requires
SS_DATABASE_SERVER=dbin.ddev/config.yaml.Linkable removed.
sheadawson/silverstripe-linkablehas no SS6 version. Replace withsilverstripe/linkfield ^5(the 4.x line is CMS 5 only; run the linkable data migration on SS5 with linkfield^4BEFORE the SS6 bump). Template API changes documented above.Embedfield replaced.
nathancox/embedfieldhas no SS6 version. Replace withfromholdio/silverstripe-embedfield ^5.1.
CMSPageAddControllerremoved. Any module extension bound to it (commonly anupdatePageOptions(FieldList $fields)hook on the "Add new page" flow) is dead code with no error - the extended class no longer exists, so the hook never fires.
- Add fields to the add form: extend
SilverStripe\CMS\Forms\CMSMainAddFormwithupdateFields(FieldList $fields). The page-type field is now namedRecordType(wasPageType), anOptionsetField- insert after it.- Act on create: extend
SilverStripe\CMS\Controllers\CMSMainwithupdateDoAdd(DataObject $record, Form $form)(CMSMainAddForm::doAdd()fires$controller->extend('updateDoAdd', $record, $form)).- Read the submitted values:
$form->getData()- by the timeupdateDoAddfires,FormRequestHandler::httpSubmission()has already populated the form from the POST vars vialoadDataFrom(), sogetData()reflects the submission, not stale construction-time data (verified againstsilverstripe/frameworkandsilverstripe/cmsSS6 source:CMSMainAddForm extends Form, no override of the base submission flow).Form::getRequestData()does not exist in any SilverStripe version - do not use it.- Verify in the live CMS, not just a unit test that calls the hook method directly - the wiring (which class the extension binds to) is exactly what silently breaks, and a direct method-call test passes regardless of the binding.
Real-world hit:
dynamic/silverstripe-elemental-templates(its template-picker "Step 3" dropdown), fixed indynamic/silverstripe-elemental-templates#83/#84.
Phase 7: Code Quality
See references/code-quality.md for PHPCS/PHPStan steps.
PHPStan vendor path: Only scan
app/srcandapp/tests— never includevendor/inphpstan.neonpaths. Vendor errors such asmethod.childReturnTypecannot be baselined (PHPStan re-reports them regardless). File upstream bugs in the relevant module repo. Full details in references/code-quality.md.
Evidence gate (Phase 7): paste the summary line of each tool run (PHPUnit test/assertion counts, PHPCS error count, PHPStan error count). "Probably still passes" does not clear this gate; only a run in this working tree does.
Phase 8: Commit & PR
Conventional commit format, checklist PR body.
Phase 9: UAT
Deploy to UAT, run migration tasks, verify frontend and CMS, troubleshoot.
Phase 10: Production
10.1 Deployment sequencing decision
If your SS6 upgrade also bundles AI modules, pre-1.0 packages, or other high-risk additions, consider splitting into two deploys — SS6 cutover first, followed by the new modules. Stacking pre-release packages on a cutover deploy increases blast radius and makes rollback more complex.
10.2 DeployHQ (or comparable CD) repoint
The deploy branch must be changed from the old SS5 branch (e.g., 4 or 5) to master (SS6). Do this right before deploying, not in advance — the old branch is the rollback target.
Pre-deploy checks:
# Verify composer install succeeds
composer install --no-dev
# Verify all runtime deps are in "require", not "require-dev"
# Notably: silverstripe/htmleditor-tinymce must be in "require"
composer show --direct | grep htmleditor
Private repo access: If the upgrade adds modules from private GitHub repos (e.g., silverstripeltd/ai-*), ensure the production server has an SSH deploy key with read access to the organization before cutover day. Test with a manual composer install if possible.
10.3 Database strategy
Option A (recommended): Push local/UAT DB to prod before cutover. Run migrations locally, then use deploy.sh --db to push the already-migrated database to production. No migration tasks needed on prod — it's a drop-in replacement.
Option B: Run migration on production's existing DB. Deploy SS6 code, run dev/build flush=1, then run each migration task sequentially. Riskier — if a migration fails mid-way, the site is partially broken and requires schema repair.
10.4 Post-deploy smoke test
# Key pages load
for u in / /work /blog /admin/ /about/about-us; do
curl -sI "https://$DOMAIN$u" | head -3
done
# Verify no redirect loop
curl -sI "https://$DOMAIN/" | grep -c "301\|302" # should be 0
# Verify CMS renders (WYSIWYG check — log in, edit a page)
# Expected: data-editor="tinyMCE" on Content field
10.5 Expected post-deploy behaviors
- All users logged out on first deploy — SS6's
SameSite=Strictsession cookie default invalidates existing sessions. Normal and expected. - TinyMCE missing → plain textarea — add
silverstripe/htmleditor-tinymce ^1.0torequire. - Migration tasks not found
dev/build flush=1not yet run, orcommandNameproperty missing from the Symfony Console task. - Custom
forTemplate()methods crash — add: stringreturn type.
Reference Documentation
| Topic | File |
|---|---|
| Data Migration Tasks | data-migration-tasks.md |
| SS5 Version Map | version-map.md |
| SS6 Version Map | version-map-ss6.md |
| Code Quality | code-quality.md |
Recipe Branch Convention (SS6)
When branching repositories for an SS6 upgrade, use:
| CMS Version | Recipe Branch | Elemental Branch | Base-site Branch |
|---|---|---|---|
| SS4 | 1 |
master (deprecated) |
5 |
| SS5 | 2 |
5 |
7 |
| SS6 | 3 |
6 |
8 |
Recipes follow recipe-cms major version numbering. Elemental modules follow their own major version (elemental ^6 = branch 6). Set the new version branch as the default branch on GitHub for each recipe/module repo.
Related skills
- silverstripe-3-to-4-upgrade — the prior, structurally different leg. Use it for legacy SS3 → SS4 projects before this skill applies.
- ss5-data-migration / ss6-data-migration — version-specific data-migration runbooks for the BuildTasks in Phase 5.
visual-regression-upgrade(from jsirish/workflow-skills, installed separately) — capture pixel diffs against the legacy instance to confirm parity (also referenced inline in the Build & Verify phase).