Skip to content

Fold demo-build learnings: hosted lying-success API traps + Swift 2.4 authoring recipes#63

Merged
Justinvolved merged 4 commits into
v2from
fold/hosted-lying-success-and-swift-authoring
Jul 24, 2026
Merged

Fold demo-build learnings: hosted lying-success API traps + Swift 2.4 authoring recipes#63
Justinvolved merged 4 commits into
v2from
fold/hosted-lying-success-and-swift-authoring

Conversation

@Justinvolved

@Justinvolved Justinvolved commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Folds a hosted-demo polish session's learnings. Version 4.11.5.

Hosted lying-success (dw-demo-base/references/online-mode.md)

  • IndexBuilderSave round-trips status: ok while the /Files/System/Repositories/** XML write is ACL-denied — assert the IndexBuilderByName readback, not the ok. Added to the hosted failure-mode list with a mandatory read-after-write on repository-config saves.
  • Full BuildIndex reads EcomGroupProductRelation through an app-lifetime cache — after a relation write the canonical order is recycle-first, then Full build; the rebuild alone no-ops and reads as "API index builds are dead on this host".
  • changeversion.txt fires on a CHANGED token, not on presence (restart ladder rung 3) — re-uploading the current value is a silent no-op; write a distinct value every time.

Commerce shop reads (foundational/commerce-catalog.md)

  • GetShopById returns a {id,name,permission} stub — the full model comes only from the namespaced GetShopByIdQuery; saving the stub clobbers the omitted fields.
  • ShopSave without an explicit UsageType defaults ShopType=0 (none), hiding the shop from every typed admin list. Pointer added from dw-demo-pim canonical-setup-order step 2.

Scheduled tasks (dw-extend-scheduled-tasks/SKILL.md)

  • RunSqlScheduledTaskAddIn is write-only — no resultset, no message text, empty lastException even for SELECT 1/0. Read-verify with assertion SQL (IF <cond> RAISERROR).

Swift 2.4 authoring

  • Facet sidebar is a 2-column grid row, not a Layout-field toggle (foundational/swift-building.md §3).
  • Repeater children live in ItemType_*_Item rows via ItemList/ItemListRelation (storage-shape truth retained), and are created + edited through POST /Admin/Api/ParagraphSave — see the Correction section below (foundational/content-modelling.md §2).
  • Conditional-collapse CSS must use flat sibling :has()/:not(:has()) pairs — nested :has() is invalid and silently dropped — and needs !important to beat the Swift grid attribute-selector specificity floor (dw-demo-swift/re-skin.md).

Validator: python3 scripts/validate-skills.py → 0 errors, 0 warnings. Sanitize grep packs (names/hosts/keys, session-time, customer paths) clean across the staged diff and the commit message.


Correction — #20 repeater-child edit path (empirically re-proven)

The original "Repeater children … unreachable through the Management API — editable only by guarded SQL plus a recycle" claim (Foundry issue #20) was wrong and has been corrected.

Proven end-to-end on the Foundry harness host (Swift 2.4 baseline restored, DW 10.28.1): the admin Visual Editor's slide editor is a SPA client of /Admin/Api. Captured its save call and replayed it headlessly (Bearer token, no browser) to CREATE a Swift-v2_Slider child slide and then EDIT it in place — no SQL, no recycle — and the storefront rendered both changes on the next GET.

Proven recipePOST /Admin/Api/ParagraphSave?Query.Type=GetParagraphById (Bearer):

  • Parent list field ContentItem|Swift-v2_Slider|General|Items[] carries child entries.
  • Child identity is ItemId: "" creates, an existing id (e.g. "1") edits in place (verified count-stable).
  • Field values ride in the ModelRawData JSON string keyed RelationItem|Swift-v2_Slider_Item|General|<Field>; RelationItem.Groups is sent empty.
  • No recycle — the save fires DW's cache invalidation.
  • ParagraphSave is a lying-success surface for this shape (a malformed child returns ok, creates nothing, and can zero the parent list pointer) → round-trip-verify.

Doctrine change — retire the SQL-via-scheduled-task / SQL-direct content motion

Eradicated the "escape to SQL (direct, or via RunSqlScheduledTaskAddIn) when the API gets hard" reflex from the demo corpus. The admin UI is API-first — capture the UI's /Admin/Api call and replay it (MCP → Management API); file a learning rather than reaching for SQL.

  • dw-demo-swift/references/sql-direct-seeding.md → deprecation stub (file kept; validator green).
  • dw-demo-swift/SKILL.md → dropped the "SQL-direct content seeding" trigger; routing rewritten (manifest regenerated).
  • foundational/data-access.md "SQL-direct content seeding" → reframed as forensic/teardown reference, not an authoring recipe.
  • dw-extend-scheduled-tasks/SKILL.md → kept the RunSql write-only / assertion-SQL platform truth as a developer diagnostic; added a guard that demo/content editing must not use it.
  • Scope note: the ERP DB-mock's between-demo RESET fixture task (dw-demo-erp) is a deliberate state-reset, not a content-authoring escape hatch — left intact.

Commits: 1d6778a (Phase B, #20 fix) · cc1b147 (Phase C, doctrine sweep).

Justinvolved and others added 4 commits July 23, 2026 19:16
… authoring recipes

A hosted-demo polish session surfaced a cluster of surfaces that report `ok` while
writing nothing on ACL-locked installs, plus three Swift 2.4 authoring recipes with no
documented home. Folds them across dw-demo-base, dw-extend-scheduled-tasks, and dw-demo-swift.

Hosted lying-success (dw-demo-base/references/online-mode.md): IndexBuilderSave round-trips
`ok` while the /Files/System/Repositories/** XML write is ACL-denied — assert the
IndexBuilderByName readback, not the status. A Full BuildIndex reads EcomGroupProductRelation
through an app-lifetime cache, so after a relation write the canonical order is recycle-first
then Full build; the rebuild alone no-ops and reads as "API index builds are dead on this host".
And changeversion.txt fires on a CHANGED token, not on presence — re-uploading the current
value is a silent no-op; write a distinct value every time.

Commerce shop reads (foundational/commerce-catalog.md): GetShopById returns a
{id,name,permission} stub — the full model comes only from the namespaced GetShopByIdQuery,
and saving the stub clobbers the omitted fields. ShopSave without an explicit UsageType defaults
ShopType=0 (none), which hides the shop from every typed admin list; pointer added from
dw-demo-pim canonical-setup-order step 2.

RunSqlScheduledTaskAddIn (dw-extend-scheduled-tasks): write-only — no resultset, no message
text, empty lastException even for SELECT 1/0. Read-verify with assertion SQL
(IF <cond> RAISERROR) — the read path when SQL is only reachable through the task addin.

Swift 2.4 authoring: the facet sidebar is a 2-column grid row, not a Layout-field toggle
(foundational/swift-building.md §3); repeater children live in ItemType_*_Item rows via
ItemList/ItemListRelation, unreachable through the Management API, editable only by guarded SQL
plus a recycle (foundational/content-modelling.md §2); conditional-collapse CSS must use flat
sibling :has()/:not(:has()) pairs — nested :has() is invalid and silently dropped — and needs
!important to beat the Swift grid attribute-selector specificity floor (dw-demo-swift/re-skin.md).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YayhziToTuSR25h6PFxpj2
…op — it recycles on R0-NET, version unchanged

Owner correction on fold #63: only a CHANGED token switches the release
ring, but a same-value re-upload still recycles the app (observed on an
R0-NET ring token) while leaving the version pinned. Rung 3 and the
4.11.5 changelog entry now state both effects: distinct token to switch
(confirm via info.version); same-value upload risks an unintended
recycle and proves nothing about the ring.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YayhziToTuSR25h6PFxpj2
…i/ParagraphSave, not SQL+recycle

Corrects the #20 fold in dw-demo-base content-modelling.md §2. The prior claim —
repeater children (Swift-v2_Slider slides, accordion items) are "unreachable
through the Management API, editable only by guarded SQL plus a recycle" — was
wrong. Proven empirically on the Foundry harness host (Swift 2.4 baseline,
DW 10.28.1): the admin Visual Editor's slide editor is a SPA client of
/Admin/Api, and its save is POST /Admin/Api/ParagraphSave. Captured the UI call
and replayed it headlessly (Bearer token, no browser) to CREATE a slide and then
EDIT it in place — no SQL, no recycle — and the storefront rendered both changes
on the next GET.

- Child identity: ContentItem|<Parent>|<Group>|Items[] entries keyed by ItemId
  (empty = create, existing id = edit in place; verified count-stable update).
- Field values ride in the ModelRawData JSON string keyed
  RelationItem|<ChildType>|<Group>|<Field>; RelationItem.Groups is sent empty.
- No recycle: ParagraphSave runs the domain service that invalidates the cache.
- Kept the storage-shape truth (ItemType_*_Item via ItemList/ItemListRelation;
  GetParagraphById collapses to Items=<listId>) — only the edit-path claim was wrong.
- ParagraphSave is a lying-success surface for this shape (a malformed child
  returns ok, creates nothing, and can zero the parent list pointer) — added a
  round-trip-verify caution.

Updates the CHANGELOG [4.11.5] repeater-child bullet accordingly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YayhziToTuSR25h6PFxpj2
…tent motion — API-first

Eradicates the "escape to SQL (direct, or via RunSqlScheduledTaskAddIn) when the
API gets hard" reflex from the demo skills. The admin UI is a SPA over
/Admin/Api: if the UI can do it, an endpoint exists — capture the network call
and replay it (MCP -> Management API), and file a learning rather than reaching
for SQL.

- dw-demo-swift/references/sql-direct-seeding.md: gutted to a deprecation stub
  pointing at the API-first rule (file kept so inbound links resolve; validator green).
- dw-demo-swift/SKILL.md: dropped the "SQL-direct content seeding" trigger and
  rewrote the routing row to the API-first path (regenerated manifest.json).
- dw-demo-base/references/foundational/data-access.md: reframed the "SQL-direct
  content seeding" section as a forensic/teardown schema reference, not an
  authoring recipe; softened the save_pages no-op fallback to a targeted local
  SQL touch-up (creation stays on the API).
- dw-extend-scheduled-tasks/SKILL.md: kept the RunSqlScheduledTaskAddIn
  write-only / assertion-SQL platform truth as a developer diagnostic; added a
  scope guard that demo/content work must not use it for edits.

Scope note: the ERP DB-mock's between-demo RESET fixture task (dw-demo-erp) is a
deliberate state-reset, not a content-authoring escape hatch, and is left intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YayhziToTuSR25h6PFxpj2
@Justinvolved
Justinvolved merged commit 4c90b6b into v2 Jul 24, 2026
1 check passed
@Justinvolved
Justinvolved deleted the fold/hosted-lying-success-and-swift-authoring branch July 24, 2026 10:47
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