# Webflow sitemap JSON import thread summary — 2026-06-02

Generated: 2026-06-02T17:12:51+1000

Slack thread context: Sylvain asked whether Hermes could pick up OpenClaw's temporary UI for visualising JSON sitemaps, published at:

- https://iggys-mac-mini.tail2bf9a2.ts.net/sitemap-builder/?v=20260602c

Primary workspace/project located during the thread:

- `/Users/iggy/.openclaw/workspaces/iggy-ignite/tools.igniteagency.com/sitemap-builder/`

Relevant saved sitemap JSON used in testing:

- `/Users/iggy/.openclaw/workspaces/iggy-ignite/tools.igniteagency.com/sitemap-builder/saved/sitemap-map-20260602-053041.json`

Relevant generated scripts shared during debugging:

- Dry run: `/Users/iggy/.hermes/profiles/ignite_team/outbound/sitemap-build-20260602-053041-dry-run.js`
- Live v1–v9 scripts: `/Users/iggy/.hermes/profiles/ignite_team/outbound/sitemap-build-20260602-053041-live-v*.js`
- Current working/latest live script: `/Users/iggy/.hermes/profiles/ignite_team/outbound/sitemap-build-20260602-053041-live-v9.js`

Important reference/skills updated during the thread:

- Hermes skill reference updated: `webflow-designer-copilot`, file `references/sitemap-json-builds.md`
- OpenClaw skill referenced by Sylvain: `~/.openclaw/skills/webflow-component-instance-props/SKILL.md`
- Starter pipeline reference doc: `/Users/iggy/.openclaw/workspaces/iggy-ignite/devlink-starter-prototype/docs/starter-json-build-pipeline.md`
- Current stored starter contract found: `/Users/iggy/.openclaw/workspaces/iggy-ignite/artifacts/starter-json/2026-05-27/starter-component-contract.json`

---

## What we solved

### 1. Found and shared the project/files

OpenClaw's sitemap builder project was found under:

```text
/Users/iggy/.openclaw/workspaces/iggy-ignite/tools.igniteagency.com/sitemap-builder/
```

The dry-run JS initially existed at:

```text
/Users/iggy/.openclaw/workspaces/iggy-ignite/tools.igniteagency.com/sitemap-builder/builds/sitemap-build-20260602-053041-dry-run.js
```

Because Slack did not show the first `MEDIA:` upload, files were copied to the Hermes outbound folder and shared with `.zip` fallbacks:

```text
/Users/iggy/.hermes/profiles/ignite_team/outbound/
```

This matches the IGNITE profile convention that JS files shared through Slack should be put in the outbound folder and zipped as a fallback.

### 2. Confirmed dry-run validation worked

The dry-run report showed the JSON was structurally valid:

```json
{
  "valid": true,
  "errors": 0,
  "warnings": 0,
  "pagesToCreate": 3,
  "pagesMatched": 0,
  "foldersToCreate": 0,
  "foldersMatched": 0,
  "structuresToInsert": 3,
  "componentsToInsert": 16,
  "missingComponents": 0
}
```

Dry run confirmed the intended build:

- Pages:
  - Home `/`
  - Learning `/learning`
  - Student Life `/student-life`
- Page structures:
  - 3 × `Template / PS - Empty Page Structure`
- Components:
  - 16 total planned components/child components

Dry run was explicitly non-mutating: no page creation, no component insertion, no publishing.

### 3. Fixed page creation / matching

Live v1 created pages but failed to append structures/components because Webflow returned inspectable component catalogue/proxy objects that could not be appended directly.

Error:

```text
Missing backing object for instance ID ...
```

Fix applied in v2+:

- Resolve exact component by name.
- Hydrate with `webflow.getComponent(component.id)` before appending.

Result:

- Home matched.
- Learning and Student Life were created.
- Later runs correctly matched all 3 pages after they existed.

### 4. Fixed page-structure insertion and page slot handling

We learned the critical Webflow structure rule:

- The page structure component **does not and cannot contain the page slot DropTarget**.
- A Webflow component cannot be created with that page slot element inside it.
- After inserting/unlinking `Template / PS - Empty Page Structure`, the script must create an explicit DropTarget for `pageSlot` under the main wrapper when page-body content is needed.

Also clarified:

- `abovePageSlot` is **not** a slot/DropTarget.
- `abovePageSlot` is only an ordering group: components that should be inserted before the page slot.
- `belowPageSlot` should be treated similarly as content after the page slot.

Working page-level behaviour:

1. Insert `Template / PS - Empty Page Structure`.
2. Unlink it where supported.
3. Insert `abovePageSlot` components directly into the main wrapper before page-slot DropTarget.
4. Create a plain, unmarked DropTarget for `pageSlot`.
5. Insert `pageSlot` components into that DropTarget.
6. Insert any `belowPageSlot` components after the page slot.

Removed later:

- Unnecessary `[data-ignite-slot]` marker/attribute on the created page slot DropTarget.

### 5. Fixed component-slot insertion

Initial attempts tried to insert child components into component slots using guessed forms such as:

```js
parentInstance.append(component, { slot: slotName })
parentInstance.append(component, { slotName })
parentInstance.append(component, slotName)
```

Those did not work reliably.

Using the OpenClaw skill `webflow-component-instance-props`, we confirmed the right model:

- Component slots already exist on the component instance.
- Do **not** create DropTargets inside components like Cards/FAQs.
- Use `parentInstance.getSlots()`.
- Match the slot by label/name/id, or if JSON says generic `Slot` and there is one slot, use that slot.
- Append child component through the slot object.

Working approach:

```js
const slots = await parentInstance.getSlots();
const slot = findMatchingSlot(slots, "Slot");
await slot.append(childComponent);
```

Additional runtime lesson:

- `slot.append(component)` may return `undefined` even when insertion succeeds.
- To recover the inserted child instance for prop setting, read `slot.getChildren()` before and after append and treat the newest child as the inserted instance.

This solved nested component insertion for:

- `Element / Card Item` children inside `Section / Cards`
- `Component / Accordion Item` children inside `Section / FAQs`

### 6. Fixed most component prop setting

Initial prop setting used the wrong shape and labels. The script originally tried object-keyed payloads, roughly:

```js
instance.setProps({ [prop.id]: value })
```

Correct Webflow component instance prop workflow:

1. Use `instance.searchProps()` for live metadata.
2. Match JSON editor-facing prop labels to `prop.display.label`, `prop.label`, `prop.name`, `prop.propId`, or `prop.id` as fallback.
3. Use `prop.propId` as the ID.
4. Set props with an array:

```js
await instance.setProps([
  { propId: "PROP_ID", value }
]);
```

5. Shape values by `valueType`:

- `textContent` → string
- `richText` → rich text object shape (see below)
- `link` → Webflow link object, e.g. `{ mode: "url", to: "/contact" }`
- `boolean` → boolean
- `number` → number
- `variant` / `headingTag` → option ID when options exist

Additional safeguard added:

- Set one prop at a time, so one invalid prop value shape does not abort the entire page build.

### 7. Solved rich text shape after multiple iterations

Rich text was the last major runtime issue.

Failed guesses:

```js
"Plain string"
{ html: "<p>Plain text</p>" }
{ innerText: "Plain text", objectType: "Object" }
```

Sylvain inspected `getProps()` on the card component and found rich text prop default shape:

```json
{
  "id": "b8b6c85b-b931-7713-e63e-af6f97f5505f",
  "type": "richText",
  "valueType": "richText",
  "bindableTo": ["richText"],
  "name": "Paragraph",
  "group": "Text",
  "defaultValue": {
    "innerText": "The rich text element allows you to create and format headings, paragraphs, blockquotes, images, and video all in one place instead of having to add and format them individually. Just double-click and easily create content."
  }
}
```

Latest v9 script uses primary richText shape:

```js
{ innerText: "Plain text" }
```

v9 also tries fallback wrappers one at a time if needed, but Sylvain confirmed v9 works.

### 8. Confirmed current script state

Latest confirmation from Sylvain:

> ok that works. there are still some unresolved prop groups but we can figure that out later.

Current best script:

```text
/Users/iggy/.hermes/profiles/ignite_team/outbound/sitemap-build-20260602-053041-live-v9.js
```

Current known status:

- Top-level page creation/matching works.
- Page structure insertion/unlinking works.
- Explicit page-slot DropTarget creation works.
- Above-page-slot ordering group works.
- Component slots now work.
- Most props set correctly.
- Rich text props work in v9.
- Some unresolved prop groups remain, likely due to JSON labels not matching actual Starter prop labels.

---

## What remains unresolved

### 1. Remaining unresolved prop groups

Known examples from earlier logs:

- `Section / Intro Text`
  - JSON had `Heading`, but live props showed only:
    - Hide section
    - id
    - Theme
    - Config
    - Paragraph
  - So `Heading` is not a valid prop for that component in the current Starter contract/runtime.

- `Section / FAQs`
  - JSON had `Text`, `Button Text`, `Link`.
  - Live props showed:
    - Primary Button Link
    - Primary Button Text
    - Hide section
    - id
    - Paragraph
    - Heading
  - Needs label normalisation or authoring using exact contract labels.

These should be handled at the content-assignment/validation layer before live build.

### 2. Need a proper content assignment process

We solved the Webflow execution layer, but the larger value is site-wide content planning/import:

- Which section/component goes on each page?
- Which exact component props should receive which content?
- How do we author content across a large site with many pages/props?
- How do we validate against the current Starter component contract before import?
- How do we review the generated content before Webflow writes?

### 3. Need reliable starter component knowledge storage

Fresh-per-run discovery alone is not enough because the planning/content UI needs to know exact props before authoring. The correct model is a versioned component knowledge cache/contract.

Sylvain clarified the actual flow:

1. In a live IGNITE Starter Designer session, run the Iggy Webflow app command:

```json
{
  "action": "exportComponentCatalogue",
  "args": {
    "limit": 250,
    "includePageInstances": true,
    "includeSnapshots": false
  }
}
```

2. Save the output here:

```text
artifacts/starter-json/<date>/native-component-catalogue.json
```

3. Generate/update the clean contract with:

```bash
node devlink-starter-prototype/scripts/normalise-starter-catalogue.mjs artifacts/starter-json/<date>/native-component-catalogue.json artifacts/starter-json/<date>/starter-component-contract.json
```

4. Use that contract for planning/content import, then validate against live Designer metadata during build.

Current stored contract:

```text
artifacts/starter-json/2026-05-27/starter-component-contract.json
```

Reference doc:

```text
devlink-starter-prototype/docs/starter-json-build-pipeline.md
```

### 4. Need build-script generator integration

The working v9 logic needs to be moved out of ad hoc generated files and into the sitemap builder / build command generator path, so future JSON can generate working JS directly.

Need to check/update likely areas:

- `tools.igniteagency.com/sitemap-builder/` generation logic
- `devlink-starter-prototype/scripts/generate-starter-build-command.mjs` or equivalent builder generator
- Contract validation scripts
- UI/export path for saved sitemap JSON

---

## Proposed plan going forward

### Phase 1 — Stabilise the Webflow execution interpreter

Codify the v9 runtime lessons in the actual generator/interpreter:

1. Resolve exact components by name.
2. Hydrate catalogue/proxy component objects with `webflow.getComponent(component.id)`.
3. Match/create pages and folders only from explicit JSON.
4. Insert/unlink `Template / PS - Empty Page Structure`.
5. Treat `abovePageSlot` as an ordering group, not a slot.
6. Create one plain page-slot DropTarget under the main wrapper when needed.
7. Insert `pageSlot` components into that DropTarget.
8. Treat component slots separately:
   - use `parentInstance.getSlots()`
   - match slot by label/name/id or single generic slot
   - call `slot.append(component)`
   - recover inserted child via `slot.getChildren()` before/after if append returns void
9. Set props with:

```js
instance.setProps([{ propId, value }])
```

10. Shape values from `valueType`.
11. Use `{ innerText }` for `richText`.
12. Set props one at a time and report failures without aborting the whole page.

### Phase 2 — Make Starter component contract the planning source of truth

Maintain a versioned starter component contract as the allowed component/prop universe.

Contract should expose:

- component name
- stable component key, e.g. `section.hero`
- current Webflow component ID if useful, but do not trust IDs across sites
- props:
  - label
  - propId/id
  - valueType
  - group
  - options/defaults
- slots:
  - labels/IDs
  - allowed child component types where known

The content UI/AI planner should author against this contract, not against guessed prop names.

### Phase 3 — Add a content assignment layer

For large sites, split planning into stages:

1. Site/page structure.
2. Page goals and audience.
3. Section/component plan per page.
4. Content assignment to exact component props.
5. Validation.
6. Review matrix.
7. Dry run.
8. Live import.

Example content assignment shape:

```json
{
  "page": "Learning",
  "sections": [
    {
      "component": "section.hero",
      "intent": "Introduce the Learning section",
      "props": {
        "Heading": "Learning that grows with each student",
        "Paragraph": {
          "innerText": "A clear introduction shaped for prospective families."
        }
      }
    }
  ]
}
```

The builder should resolve `section.hero` and editor-facing prop labels to actual Webflow prop IDs using `starter-component-contract.json`.

### Phase 4 — Add validation before live import

Validation should catch issues like the unresolved prop examples before Webflow writes.

Validation checks:

- component key/name exists in contract
- prop labels exist on that component
- value shape matches `valueType`
- rich text uses `{ innerText }`
- links are valid link objects
- variants/options are valid
- nested slot children are allowed/known
- placeholders/generic content are rejected
- internal links point to planned pages
- CTA labels/links are consistent

Validation should provide suggestions, e.g.:

```json
{
  "component": "Section / FAQs",
  "prop": "Button Text",
  "issue": "Prop does not exist",
  "suggestion": "Primary Button Text"
}
```

### Phase 5 — Build a reviewable content matrix

For large sites, JSON alone is not review-friendly. Generate a content matrix/table from the assignment layer:

| Page | Section | Component | Prop | Content | Source | Status |
|---|---|---|---|---|---|---|
| Learning | Hero | Section / Hero | Heading | Learning that grows with each student | AI draft | Needs review |
| Learning | CTA | Section / CTA | Primary Button Text | Book a tour | Global CTA rule | Approved |

This could be:

- inside the sitemap builder UI
- a generated HTML review page
- Google Sheets
- JSON plus rendered preview

### Phase 6 — Site-wide content rules

Define rules that the assignment layer can apply consistently:

- global CTAs
- tone/voice rules
- banned generic phrases
- link conventions
- section-specific copy length and purpose
- page-type templates
- card/FAQ repeat patterns

Example:

```json
{
  "globalCtas": {
    "primaryEnrolment": {
      "text": "Book a tour",
      "link": "/enrolments"
    }
  },
  "tone": {
    "avoid": ["world-class", "nurturing environment", "holistic education"],
    "prefer": ["specific proof", "clear parent benefit", "plain English"]
  }
}
```

---

## Recommended artefact model

Keep each phase as an explicit artefact:

```text
artifacts/starter-json/YYYY-MM-DD/
  native-component-catalogue.json
  starter-component-contract.json
  site-structure.json
  section-plan.json
  content-assignment.json
  content-assignment.validation.json
  content-review-matrix.html or .csv
  build-command.json
  webflow-build-script.js
  webflow-build-report.json
```

This keeps planning, validation, review, and Webflow execution separate and reproducible.

---

## Current best answer to “what process do we take?”

Use the Starter contract as the source of truth, then build a content-first but contract-aware pipeline:

1. Export/update native Starter component catalogue.
2. Normalise to starter component contract.
3. Generate/confirm sitemap and page goals.
4. Generate section plan per page.
5. Assign content to actual component props using the contract.
6. Validate prop names, value types, links, slots and placeholders.
7. Review content in a matrix/table before import.
8. Generate dry-run Webflow build report.
9. Generate/run live import using v9 interpreter logic.
10. Run post-build audit and capture unresolved items.

The real benefit for large sites is site-wide authoring and validation before Webflow writes, not only generating the final JS.

---

## Pickup checklist for next session

1. Open this summary.
2. Review the updated skill reference: `webflow-designer-copilot` → `references/sitemap-json-builds.md`.
3. Inspect current v9 script:
   - `/Users/iggy/.hermes/profiles/ignite_team/outbound/sitemap-build-20260602-053041-live-v9.js`
4. Move the v9 interpreter logic into the actual sitemap builder/build generator.
5. Inspect/update Starter component contract generation:
   - `devlink-starter-prototype/scripts/normalise-starter-catalogue.mjs`
   - `devlink-starter-prototype/docs/starter-json-build-pipeline.md`
6. Build validation for content assignment against:
   - `artifacts/starter-json/2026-05-27/starter-component-contract.json`
7. Add/plan UI for content assignment matrix and validation report.
