Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .agents/skills/qa-engineer-manual/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,32 @@ You seed Storyblok QA spaces with predefined test scenarios. Packages might defi
- **Trust the seed's exit status, not just its log.** The seed now verifies each staged resource
actually landed remotely and exits non-zero with a `Verification FAILED` message when a push
reports success but creates nothing, so check the exit status.
- **A green seed proves the API accepted your JSON, not that the JSON is shaped like something the
editor would produce.** The Management API stores component schemas and story content as opaque
blobs: it takes almost any structure and echoes it back. Seeding a fixture and reading it back is
circular, because you authored the input, so it can never validate a shape. It is still a useful
fixed-point check, and it can prove server-side _transformation_ (normalization, defaults,
rejection) - just never correctness.

### Grounding a shape

When you need to know what a field option or content value really looks like, work down this list
and stop at the first step that answers it:

1. **Existing seeds.** Check the scenario fixtures for a field or value of that type. The corpus is
kept aligned with real wire formats, so a shape already in there is usually your answer.
2. **`../storyrails`.** The backend truth for what is stored, normalized, and enforced.
`spec/integration/openapi/` is the source of truth for schemas and error shapes.
3. **`../storyfront`.** The editor is what writes component schemas and content values:
`packages/openapi/src/FieldType.ts` declares the shapes, `Schema/*/index.vue` is what it
**writes**, `EditorForm/FieldType*/` is what it **reads**.
4. **Ask the user to author it in the UI.** Last resort, when the source does not settle it. Ask
them to create the field or story by hand in a QA space and give you the id, then read it back
via MAPI (`curl` the component or story) and copy the result into the fixture verbatim. This is
the only empirical evidence that exists - everything you push yourself is circular.

Never infer a shape from the schema form alone: the editor offers options it does not write, and
spaces hold keys the editor no longer offers.

### Prerequisites

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,35 @@
"is_root": true,
"is_nestable": false,
"schema": {
"title": { "type": "text", "pos": 0 },
"body": { "type": "richtext", "pos": 1 },
"featured_image": { "type": "asset", "pos": 2, "filetypes": ["images"] },
"author": { "type": "text", "pos": 3 },
"title": {
"type": "text",
"pos": 0
},
"body": {
"type": "richtext",
"pos": 1
},
"featured_image": {
"type": "asset",
"pos": 2,
"filetypes": ["images"]
},
"author": {
"type": "text",
"pos": 3
},
"category": {
"type": "option",
"pos": 4,
"source": "internal_stories",
"source": "internal",
"datasource_slug": "categories"
},
"related_post": { "type": "multilink", "pos": 5, "restrict_content_types": true }
"related_post": {
"type": "multilink",
"pos": 5,
"restrict_content_types": true,
"component_whitelist": ["blog"]
}
},
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-01-01T00:00:00.000Z"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,27 @@
"is_root": true,
"is_nestable": false,
"schema": {
"headline": { "type": "text", "pos": 0 },
"headline": {
"type": "text",
"pos": 0
},
"body": {
"type": "bloks",
"pos": 1,
"restrict_components": true,
"component_whitelist": ["hero", "cta"]
"component_whitelist": ["hero", "cta"],
"restrict_type": "",
"component_denylist": []
},
"hero_image": {
"type": "asset",
"pos": 2,
"filetypes": ["images"]
},
"hero_image": { "type": "asset", "pos": 2, "filetypes": ["images"] },
"seo_description": { "type": "textarea", "pos": 3 }
"seo_description": {
"type": "textarea",
"pos": 3
}
},
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-01-01T00:00:00.000Z"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"id": 2,
"short_filename": "blog-cover.png",
"alt": "Blog cover",
"title": "Blog Cover",
"copyright": "",
"source": "",
"focus": "",
"meta_data": {
"alt": "Blog cover",
"title": "Blog Cover",
"copyright": "",
"source": ""
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"id": 1,
"short_filename": "hero.png",
"alt": "Hero image",
"title": "Hero",
"copyright": "",
"source": "",
"focus": "",
"meta_data": {
"alt": "Hero image",
"title": "Hero",
"copyright": "",
"source": ""
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"slug": "categories",
"dimensions": [],
"entries": [
{ "id": 1, "name": "Technology", "value": "technology", "dimension_value": null },
{ "id": 2, "name": "Design", "value": "design", "dimension_value": null }
{ "id": 1, "name": "Technology", "value": "technology", "dimension_value": "" },
{ "id": 2, "name": "Design", "value": "design", "dimension_value": "" }
]
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,60 @@
{
"id": 2,
"uuid": "2",
"uuid": "b2c5d3e1-82e2-4a3b-9c4d-1b6e7f809102",
"name": "About",
"slug": "about",
"full_slug": "about",
"content": {
"_uid": "b6a1f791-3aac-4aee-8469-b8576132a6c6",
"component": "page",
"headline": "About Us",
"hero_image": {
"id": 2,
"name": "",
"filename": "https://a.storyblok.com/f/1/1200x600/bbbbbbbbbb/blog-cover.png",
"alt": "About hero",
"title": "Blog Cover",
"source": "",
"copyright": "",
"focus": "",
"fieldtype": "asset",
"filename": "https://placeholder.example.com/blog-cover.png",
"alt": "About hero"
"is_external_url": false,
"meta_data": {
"alt": "About hero",
"title": "Blog Cover",
"source": "",
"copyright": "",
"size": "1200x600"
}
},
"body": [
{
"_uid": "fd2f2548-e919-49c9-884f-15e7c2488aa2",
"component": "hero",
"_uid": "hero-about-1",
"title": "Who We Are",
"image": {
"id": 2,
"name": "",
"filename": "https://a.storyblok.com/f/1/1200x600/bbbbbbbbbb/blog-cover.png",
"alt": "About image",
"title": "Blog Cover",
"source": "",
"copyright": "",
"focus": "",
"fieldtype": "asset",
"filename": "https://placeholder.example.com/blog-cover.png",
"alt": "About image"
"is_external_url": false,
"meta_data": {
"alt": "About image",
"title": "Blog Cover",
"source": "",
"copyright": "",
"size": "1200x600"
}
},
"link": {
"fieldtype": "multilink",
"linktype": "story",
"id": "1",
"id": "a1f4e2c0-91d1-4f2a-8b3c-0a5d6e7f8091",
"url": "",
"cached_url": "home"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
{
"id": 7,
"uuid": "7",
"uuid": "07182836-37d7-4f80-8192-60b324354657",
"name": "Color Theory for Teams",
"slug": "blog-color-theory",
"full_slug": "blog-index/blog-color-theory",
"parent_id": 3,
"content": {
"_uid": "7ba46cc6-0afe-4aab-866b-9b479d0157ea",
"component": "blog",
"title": "Color Theory for Teams",
"featured_image": {
"id": 2,
"name": "",
"filename": "https://a.storyblok.com/f/1/1200x600/bbbbbbbbbb/blog-cover.png",
"alt": "Color theory",
"title": "Blog Cover",
"source": "",
"copyright": "",
"focus": "",
"fieldtype": "asset",
"filename": "https://placeholder.example.com/blog-cover.png",
"alt": "Color theory"
"is_external_url": false,
"meta_data": {
"alt": "Color theory",
"title": "Blog Cover",
"source": "",
"copyright": "",
"size": "1200x600"
}
},
"author": "QA Bot",
"category": "design",
"related_post": {
"fieldtype": "multilink",
"linktype": "story",
"id": "5",
"id": "e5f80614-55b5-4d6e-8f70-4e91a2132435",
"url": "",
"cached_url": "blog-index/blog-design-tips"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
{
"id": 5,
"uuid": "5",
"uuid": "e5f80614-55b5-4d6e-8f70-4e91a2132435",
"name": "Design Tips for Better UX",
"slug": "blog-design-tips",
"full_slug": "blog-index/blog-design-tips",
"parent_id": 3,
"content": {
"_uid": "64ea3210-f1eb-40fa-815d-370be17b251a",
"component": "blog",
"title": "Design Tips for Better UX",
"featured_image": {
"id": 2,
"name": "",
"filename": "https://a.storyblok.com/f/1/1200x600/bbbbbbbbbb/blog-cover.png",
"alt": "Design",
"title": "Blog Cover",
"source": "",
"copyright": "",
"focus": "",
"fieldtype": "asset",
"filename": "https://placeholder.example.com/blog-cover.png",
"alt": "Design"
"is_external_url": false,
"meta_data": {
"alt": "Design",
"title": "Blog Cover",
"source": "",
"copyright": "",
"size": "1200x600"
}
},
"author": "QA Bot",
"category": "design",
"related_post": {
"fieldtype": "multilink",
"linktype": "story",
"id": "4",
"id": "d4e7f503-64a4-4c5d-9e6f-3d8091a21324",
"url": "",
"cached_url": "blog-index/blog-tech-intro"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
{
"id": 6,
"uuid": "6",
"uuid": "f6091725-46c6-4e7f-9081-5fa213243546",
"name": "Blog Getting Started",
"slug": "blog-getting-started",
"full_slug": "blog-index/blog-getting-started",
"parent_id": 3,
"content": {
"_uid": "72928fd8-9b68-4bd4-853d-90ed34768f2f",
"component": "blog",
"title": "Blog Getting Started",
"featured_image": {
"id": 1,
"name": "",
"filename": "https://a.storyblok.com/f/1/1200x600/aaaaaaaaaa/hero.png",
"alt": "Start",
"title": "Hero",
"source": "",
"copyright": "",
"focus": "",
"fieldtype": "asset",
"filename": "https://placeholder.example.com/hero.png",
"alt": "Start"
"is_external_url": false,
"meta_data": {
"alt": "Start",
"title": "Hero",
"source": "",
"copyright": "",
"size": "1200x600"
}
},
"author": "QA Bot",
"category": "technology",
"related_post": {
"fieldtype": "multilink",
"linktype": "story",
"id": "4",
"id": "d4e7f503-64a4-4c5d-9e6f-3d8091a21324",
"url": "",
"cached_url": "blog-index/blog-tech-intro"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
{
"id": 3,
"uuid": "3",
"uuid": "c3d6e4f2-73f3-4b4c-8d5e-2c7f8091a213",
"name": "Blog",
"slug": "blog-index",
"full_slug": "blog-index",
"is_folder": true,
"content": {
"component": "page",
"headline": "Blog",
"hero_image": {
"id": 1,
"fieldtype": "asset",
"filename": "https://placeholder.example.com/hero.png",
"alt": "Blog hero"
},
"seo_description": "Our blog"
}
"content_types": ["blog"],
"lock_subfolders_content_types": true
},
"default_root": "blog"
}
Loading
Loading