Skip to content

Fold hosted-publish learnings: split publish-to-hosted.md, correct upload + mode enum#58

Merged
Justinvolved merged 1 commit into
v2from
fold/publish-to-hosted
Jul 13, 2026
Merged

Fold hosted-publish learnings: split publish-to-hosted.md, correct upload + mode enum#58
Justinvolved merged 1 commit into
v2from
fold/publish-to-hosted

Conversation

@Justinvolved

Copy link
Copy Markdown
Collaborator

What surfaced this

A second local→hosted publish. One serializer gap had an outsized blast radius, and two pieces of existing guidance turned out to be wrong.

The headline — a silent, install-wide index kill

Custom product fields are column-backed: EcomProductField is a physical column on EcomProducts. A SqlTable predicate deserializes the definition rows faithfully, and nothing creates the columns — the engine's schema-sync only walks EcomProductGroupField.

The target then lands with field definitions whose columns do not exist, and:

  • every product read returns HTTP 500 whose title is the bare field name;
  • every Full index build returns status: ok while indexing zero documents — install-wide, including repositories the demo never touched;
  • the PLP renders an empty grid while still printing its product count.

Only the first of those looks like an error. BuildIndex reporting ok is what makes this expensive.

It also deadlocks: once the definitions exist without columns, the field can be neither dropped (ALTER TABLE DROP COLUMN failed because column '<F>' does not exist) nor created (system name already in use). The one exit — re-saving the existing field provisions the column — is documented, along with the duplicate-SystemName trap that reproduces the same zero-document failure if you create the field via the API and then deserialize.

Pre-flight fix: create the product fields on the target with ProductFieldSave before the first deserialize.

Routed to the engine as well — the durable fix is to extend schema-sync to the column-backed path and to fail loudly rather than index nothing. Filed in the Foundry learnings inbox.

The split

online-mode.md had grown to cover two different jobs (build on a hosted install vs migrate a local demo onto one) and was at the size guideline. The publish playbook moves to a new references/publish-to-hosted.md, extended with:

  • Id collisions on an install that already has content. Most expensive: a variant group whose target twin is a colour group — the demo's options deserialize into it, Swift renders colour swatches, and the PDP shows no selector at all while the variant products index perfectly. Also EcomGroupRelations (the target's hierarchy wins unless you ship it) and relation rows that silently re-attach the stock catalog.
  • Indexes — the repository definition travels, the built segments do not (copying them yields a PLP with a product count and no cards). A repository uploaded into a running app needs a restart before its facets resolve.
  • Derive-on-save item fields don't survive a deserialize (logo width is the canary), and IsDryRun before every hosted deserialize.
  • Orders ride a plain SqlTable predicate, though no shipped example config includes them.

Corrections to existing guidance

Was Now
Upload never overwrites → delete before re-uploading allowOverwrite=true — an undocumented form field that works. Success is model being a list, never status: ok: a refused batch reports ok, writes nothing, and one pre-existing name drops the batch's new files too.
"A cloud install CAN be restarted" Qualified. The CloudHosting control files are a Dynamicweb Cloud affordance. A partner-hosted install accepted recycle.txt/restart.txt and never acted on them — confirm the file is consumed, or rung 3 doesn't exist on that host.
serializer-reference.md: "mode": "Deploy"|"Seed" is the current enum Version-scoped. 0.8.x accepts only Replace/MergeDeploy/Seed are rejected, not aliased (ConfigLoader.ValidatePredicates) — and the run's mode moves into the JSON body. A config authored for the wrong engine major 500s every Serializer call, including the read-only settings query, so GET /Admin/Api/SerializerSettings is now the prescribed one-call config-validity probe.

deserialize-flow.md's 0.6.9-stamped two-pass ?mode= flow is still correct for 0.6.9 and gains a pointer for 0.8.x callers.

Gates

  • python scripts/validate-skills.py0 errors, 0 warnings
  • Sanitization grep pack (engagement tokens / session-relative time / customer paths) → clean across the staged diff and the commit message
  • Supersede sweep run across skills/ for the Deploy/Seed claim — three hits, all resolved (one was already correct, one corrected, one version-pointed)
  • metadata.version 4.8.0 → 4.9.0 (minor — new reference), README + CHANGELOG updated in the same commit

…load + mode enum

A second local-to-hosted publish surfaced a serializer gap with an outsized blast
radius: custom product fields are column-backed (EcomProductField = a column on
EcomProducts) but the engine's schema-sync only walks EcomProductGroupField, so a
deserialize lands field definitions whose columns do not exist. That 500s every
product read and makes every Full index build report status:ok while indexing zero
documents — install-wide, including repositories the demo never touched. The
pre-flight (create the fields on the target before the first deserialize), the
resulting deadlock, and the duplicate-SystemName trap that reproduces the same
zero-document failure are now documented.

online-mode.md had grown to cover two different jobs. The publish playbook moves to
a new references/publish-to-hosted.md, extended with the id collisions that bite when
publishing onto a stock Swift install (a variant group whose target twin is a colour
group swallows the demo's options and renders no selector), index portability (the
definition travels, the built segments do not), and derive-on-save item fields that a
deserialize cannot preserve.

Two corrections to existing guidance. Upload: allowOverwrite=true is an undocumented
form field that makes the endpoint overwrite; the prior delete-before-upload
workaround is replaced, and success is model being a list, never status:ok — a refused
batch reports ok and writes nothing. Restart: the CloudHosting control files are a
Dynamicweb Cloud affordance, not a property of every hosted install, so the flat claim
that a cloud install can be restarted is now qualified with a consumed-file check.

serializer-reference.md taught "mode": "Deploy"|"Seed" as the current predicate enum.
Engine 0.8.x rejects those (ConfigLoader.ValidatePredicates: valid values are Replace,
Merge) and moves the run's mode into the JSON body. The enum is now version-scoped, and
GET /Admin/Api/SerializerSettings is prescribed as the one-call config-validity probe —
a config authored for the wrong engine major 500s every Serializer call, including that
read-only one. deserialize-flow.md's 0.6.9-stamped flow gains a pointer for 0.8.x callers.
@Justinvolved
Justinvolved force-pushed the fold/publish-to-hosted branch from fd87818 to 85f16c6 Compare July 13, 2026 12:13
@Justinvolved
Justinvolved merged commit 601b8f7 into v2 Jul 13, 2026
1 check passed
@Justinvolved
Justinvolved deleted the fold/publish-to-hosted branch July 13, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant