fimo 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/assets/astro/components/DateTime.astro +22 -0
  2. package/assets/astro/components/index.d.ts +1 -0
  3. package/assets/astro/components/index.js +1 -0
  4. package/assets/content-templates/shared/types.eta +2 -0
  5. package/assets/skills/fimo-cli/SKILL.md +8 -6
  6. package/assets/skills/fimo-cli/references/branches.md +2 -2
  7. package/assets/skills/fimo-cli/references/content.md +2 -2
  8. package/assets/skills/fimo-cli/references/deploy.md +17 -9
  9. package/assets/skills/fimo-cli/references/hosting.md +114 -0
  10. package/assets/skills/fimo-cli/references/init.md +13 -6
  11. package/assets/skills/fimo-cli/references/integrations/firecrawl.md +2 -0
  12. package/assets/skills/fimo-cli/references/locales.md +38 -14
  13. package/assets/skills/fimo-cli/references/pages.md +70 -0
  14. package/assets/skills/fimo-cli/references/preview.md +26 -20
  15. package/assets/skills/fimo-cli/references/webhooks.md +1 -1
  16. package/assets/skills/fimo-code/SKILL.md +11 -0
  17. package/assets/skills/fimo-code/assets/react-router-locales/LocaleLayout.tsx +12 -0
  18. package/assets/skills/fimo-code/assets/react-router-locales/routes.ts +7 -6
  19. package/assets/skills/fimo-code/references/best-practices/astro.md +47 -0
  20. package/assets/skills/fimo-code/references/best-practices/next.md +42 -0
  21. package/assets/skills/fimo-code/references/best-practices/sveltekit.md +17 -0
  22. package/assets/skills/fimo-code/references/content/richtext.md +5 -0
  23. package/assets/skills/fimo-code/references/content/schema.md +4 -1
  24. package/assets/skills/fimo-code/references/frameworks/astro.md +5 -3
  25. package/assets/skills/fimo-code/references/frameworks/hosting.md +5 -2
  26. package/assets/skills/fimo-code/references/frameworks/next/app-router.md +6 -0
  27. package/assets/skills/fimo-code/references/frameworks/next/locales.md +12 -2
  28. package/assets/skills/fimo-code/references/frameworks/next.md +7 -0
  29. package/assets/skills/fimo-code/references/frameworks/react-router.md +15 -7
  30. package/assets/skills/fimo-code/references/frameworks/sveltekit.md +15 -8
  31. package/assets/skills/fimo-code/references/labels.md +9 -1
  32. package/assets/skills/fimo-code/references/locales.md +8 -2
  33. package/assets/skills/fimo-code/references/migrations/react-router-route-ownership.md +93 -0
  34. package/assets/skills/fimo-code/references/ui.md +21 -2
  35. package/assets/skills/fimo-migration/SKILL.md +276 -51
  36. package/assets/skills/fimo-migration/references/content.md +119 -35
  37. package/assets/skills/fimo-migration/references/decision.md +72 -0
  38. package/assets/skills/fimo-migration/references/forms.md +38 -6
  39. package/assets/skills/fimo-migration/references/frameworks/nextjs.md +179 -23
  40. package/assets/skills/fimo-migration/references/import-contract.md +71 -19
  41. package/assets/skills/fimo-migration/references/manifest.md +11 -161
  42. package/assets/skills/fimo-migration/references/media.md +33 -1
  43. package/assets/skills/fimo-migration/references/platforms.md +26 -2
  44. package/assets/skills/fimo-migration/references/qa.md +167 -13
  45. package/assets/skills/fimo-migration/references/workers/content-execution.md +39 -0
  46. package/assets/skills/fimo-migration/references/workers/content-planner.md +64 -0
  47. package/assets/skills/fimo-migration/references/workers/framework.md +37 -0
  48. package/assets/skills/fimo-migration/references/workers/validator.md +25 -0
  49. package/assets/skills/fimo-migration/references/workers.md +82 -0
  50. package/assets/skills/fimo-migration/references/workflow.md +140 -192
  51. package/assets/skills/fimo-migration/scripts/apply-decision.mjs +534 -0
  52. package/assets/skills/fimo-migration/scripts/build-canonical-manifest.mjs +7 -4
  53. package/assets/skills/fimo-migration/scripts/write-migration-report.mjs +191 -4
  54. package/assets/skills/fimo-studio/references/ai-chat.md +1 -1
  55. package/assets/skills/fimo-studio/references/cms.md +25 -7
  56. package/assets/skills/fimo-studio/references/webhooks.md +1 -1
  57. package/assets/sveltekit/components/DateTime.svelte +35 -0
  58. package/assets/sveltekit/components/FimoProvider.svelte +2 -2
  59. package/assets/sveltekit/components/index.d.ts +1 -0
  60. package/assets/sveltekit/components/index.js +1 -0
  61. package/dist/build/next/config.d.ts +4 -1
  62. package/dist/build/next/config.js +21 -5
  63. package/dist/build/sveltekit/vite.d.ts +3 -4
  64. package/dist/build/sveltekit/vite.js +3 -4
  65. package/dist/build/vite/plugins/runtime-env.d.ts +2 -1
  66. package/dist/build/vite/plugins/runtime-env.js +14 -10
  67. package/dist/cli/bundle.json +2 -2
  68. package/dist/cli/index.js +113 -108
  69. package/dist/runtime/app/FimoProviders.js +25 -14
  70. package/dist/runtime/app/prefetch.d.ts +3 -1
  71. package/dist/runtime/app/prefetch.js +11 -55
  72. package/dist/runtime/astro/preview.js +7 -9
  73. package/dist/runtime/content/client/internal.js +1 -0
  74. package/dist/runtime/i18n/locale.d.ts +6 -3
  75. package/dist/runtime/i18n/locale.js +16 -41
  76. package/dist/runtime/next/client-runtime.js +5 -1
  77. package/dist/runtime/next/client.d.ts +3 -2
  78. package/dist/runtime/next/client.js +2 -1
  79. package/dist/runtime/next/labels.d.ts +2 -2
  80. package/dist/runtime/next/labels.js +2 -2
  81. package/dist/runtime/next/preview.d.ts +13 -0
  82. package/dist/runtime/next/preview.js +34 -0
  83. package/dist/runtime/next/primitives/server.d.ts +2 -0
  84. package/dist/runtime/next/primitives/server.js +8 -0
  85. package/dist/runtime/next/provider.d.ts +5 -0
  86. package/dist/runtime/next/provider.js +17 -3
  87. package/dist/runtime/next/server-runtime.js +4 -1
  88. package/dist/runtime/next/server.d.ts +1 -1
  89. package/dist/runtime/next/server.js +1 -1
  90. package/dist/runtime/paths/get-fimo-paths.d.ts +6 -5
  91. package/dist/runtime/paths/get-fimo-paths.js +43 -42
  92. package/dist/runtime/preview/location.d.ts +19 -2
  93. package/dist/runtime/preview/location.js +16 -2
  94. package/dist/runtime/primitives/index.d.ts +1 -0
  95. package/dist/runtime/primitives/index.js +1 -0
  96. package/dist/runtime/react/primitives/editable/DateTime.d.ts +4 -0
  97. package/dist/runtime/react/primitives/editable/DateTime.js +13 -0
  98. package/dist/runtime/react/primitives/renderers/DateRenderer.d.ts +7 -1
  99. package/dist/runtime/react/primitives/renderers/DateRenderer.js +11 -8
  100. package/dist/runtime/react-router/{routes.d.ts → legacy/routes.d.ts} +6 -1
  101. package/dist/runtime/react-router/{routes.js → legacy/routes.js} +22 -7
  102. package/dist/runtime/routes/index.d.ts +162 -0
  103. package/dist/runtime/routes/index.js +335 -0
  104. package/dist/runtime/routing/AppRoutesProvider.js +4 -3
  105. package/dist/runtime/seo/resolver.d.ts +5 -0
  106. package/dist/runtime/seo/resolver.js +10 -42
  107. package/dist/runtime/shared/fimo-config.types.d.ts +14 -13
  108. package/dist/runtime/shared/preview-location.d.ts +1 -22
  109. package/dist/runtime/shared/preview-location.js +5 -93
  110. package/dist/runtime/sveltekit/preview.d.ts +12 -3
  111. package/dist/runtime/sveltekit/preview.js +13 -8
  112. package/dist/runtime/templates.d.ts +19 -0
  113. package/dist/runtime/templates.js +33 -0
  114. package/dist/scripts/sync-forms.js +1 -1
  115. package/dist/scripts/sync-schemas.js +1 -4
  116. package/dist/scripts/validate-fimo-config.js +5 -1
  117. package/dist/scripts/validate-page-registry.d.ts +2 -0
  118. package/dist/scripts/validate-page-registry.js +167 -0
  119. package/dist/scripts/validate-route-metadata.js +132 -41
  120. package/package.json +10 -5
  121. package/templates/astro/.fimo/config.json +13 -5
  122. package/templates/astro/AGENTS.md +5 -1
  123. package/templates/astro/astro.config.mjs +7 -0
  124. package/templates/astro/pnpm-workspace.yaml +0 -3
  125. package/templates/astro/src/components/ProjectOverview.astro +9 -6
  126. package/templates/astro/src/layouts/Base.astro +1 -1
  127. package/templates/astro/src/locales.ts +4 -0
  128. package/templates/astro/src/pages/[locale]/index.astro +19 -0
  129. package/templates/astro/src/pages/[locale]/projects/[slug].astro +35 -0
  130. package/templates/astro/src/pages/[locale]/projects/index.astro +19 -0
  131. package/templates/astro/src/pages/index.astro +5 -2
  132. package/templates/astro/src/pages/projects/[slug].astro +4 -2
  133. package/templates/astro/src/pages/projects/index.astro +5 -2
  134. package/templates/nextjs/.fimo/config.json +2 -1
  135. package/templates/nextjs/AGENTS.md +5 -0
  136. package/templates/nextjs/src/app/(default)/layout.tsx +10 -0
  137. package/templates/nextjs/src/app/[locale]/layout.tsx +22 -0
  138. package/templates/nextjs/src/app/[locale]/page.tsx +1 -0
  139. package/templates/nextjs/src/components/Document.tsx +26 -0
  140. package/templates/nextjs/src/components/FimoPreviewBridge.tsx +13 -0
  141. package/templates/nextjs/src/locales.ts +13 -0
  142. package/templates/react-router/.fimo/config.json +3 -6
  143. package/templates/react-router/AGENTS.md +4 -0
  144. package/templates/react-router/package.json +1 -1
  145. package/templates/react-router/pnpm-workspace.yaml +1 -3
  146. package/templates/react-router/react-router.config.ts +4 -10
  147. package/templates/react-router/src/pages/LocaleLayout.tsx +12 -0
  148. package/templates/react-router/src/root.tsx +2 -4
  149. package/templates/react-router/src/routes.ts +5 -3
  150. package/templates/sveltekit/.fimo/config.json +2 -1
  151. package/templates/sveltekit/AGENTS.md +5 -0
  152. package/templates/sveltekit/src/app.html +1 -1
  153. package/templates/sveltekit/src/hooks.server.ts +11 -0
  154. package/templates/sveltekit/src/locales.ts +8 -0
  155. package/templates/sveltekit/src/params/locale.ts +5 -0
  156. package/templates/sveltekit/src/routes/+layout.server.ts +6 -1
  157. package/templates/sveltekit/src/routes/+layout.svelte +5 -0
  158. package/assets/skills/fimo-migration/references/inventory/coordinator.md +0 -40
  159. package/assets/skills/fimo-migration/references/inventory/locales/next-intl.md +0 -39
  160. package/assets/skills/fimo-migration/references/inventory/locales.md +0 -41
  161. package/assets/skills/fimo-migration/scripts/inventory/locales/next-intl.mjs +0 -459
  162. package/templates/nextjs/src/app/layout.tsx +0 -17
  163. /package/templates/nextjs/src/app/{page.tsx → (default)/page.tsx} +0 -0
  164. /package/templates/sveltekit/src/routes/{+page.svelte → [[locale=locale]]/+page.svelte} +0 -0
@@ -8,6 +8,13 @@ but it is not complete until the relevant Fimo project contains the imported
8
8
  assets, schemas, entries, and forms, or the gaps are owner-visible unresolved
9
9
  items.
10
10
 
11
+ Every data-plane write in this contract targets the linked project from
12
+ workflow Phase 0. Local artifacts never require the link — `fimo migrate
13
+ import` only prints the import plan (it writes no data) and warns in an
14
+ unlinked directory — running the printed commands is what needs the link.
15
+ When no owner-supplied target exists, the writes stay unresolved owner
16
+ actions; do not fake a link to make them pass.
17
+
11
18
  ## Media Files
12
19
 
13
20
  Discovery must include:
@@ -17,13 +24,17 @@ Discovery must include:
17
24
  - CMS media fields
18
25
  - SEO/share images
19
26
  - downloadable files linked from pages or entries
27
+ - every schema media field, including empty or unresolved source values
28
+ - image nodes nested anywhere in richtext or Tiptap payloads
20
29
 
21
30
  Import requirements:
22
31
 
23
- - every imported asset records a Fimo target asset ID or URL in
24
- `.fimo/migration/manifest.json`
32
+ - every imported asset records a Fimo target asset ID or URL in the worker and
33
+ migration reports
25
34
  - every failed asset has an unresolved item with references to the affected
26
35
  route or entry
36
+ - every planner media obligation has an uploaded mapping, an explicit
37
+ unresolved/unsupported disposition, or a proven duplicate mapping
27
38
  - CMS media fields are rewritten after upload; source URLs are not left in
28
39
  editable entry payloads unless explicitly unresolved
29
40
 
@@ -46,10 +57,14 @@ Discovery must include:
46
57
 
47
58
  Import requirements:
48
59
 
49
- - schemas are pushed before entries
60
+ - schemas are validated and pushed before media or content writes
61
+ - `fimo schemas push` reconciles an ambiguous 504 by reading every requested
62
+ schema back. A complete match counts as committed; when all are absent it
63
+ retries once; mixed, mismatched, or still-absent state stops for review
64
+ instead of replaying another blind write.
50
65
  - entries are created or updated in Fimo, not only written as seed JSON
51
66
  - source count, generated count, imported count, skipped count, and unresolved
52
- count are recorded per content type
67
+ count are recorded per content type in the text plan/report
53
68
  - references are resolved in a second pass only when stable IDs or confirmed
54
69
  mappings exist
55
70
 
@@ -72,32 +87,69 @@ Discovery must include:
72
87
 
73
88
  Import requirements:
74
89
 
75
- - simple contact/newsletter/lead forms become `src/forms/*.json` and are pushed
76
- to Fimo
77
- - third-party or unsupported forms are recorded with source action, reason, and
78
- owner action
90
+ - a form becomes `src/forms/*.json` and is pushed to Fimo only when the Fimo
91
+ form reproduces the source form's user-visible behavior end to end
92
+ - a third-party form whose behavior is not 1:1 reproducible keeps its original
93
+ integration wired, with the required credential environment keys recorded by
94
+ name (never their values) and any missing secret reported as a user
95
+ follow-up
96
+ - unsupported forms are recorded with source action, reason, and owner action
79
97
  - the report records form count, Fimo-native count, third-party count, and
80
98
  unsupported/unresolved count
81
99
 
82
100
  Acceptance:
83
101
 
84
- - every visible source form is represented by a Fimo form, third-party plan, or
85
- unsupported/unresolved item
102
+ - every visible source form is represented by a Fimo form, a preserved
103
+ third-party integration, or an unsupported/unresolved item
86
104
  - no page keeps a broken form UI that appears live
87
105
 
106
+ Historical form submissions do not have a write contract. The current
107
+ form-submissions CLI is read-only, so never claim that old submissions were
108
+ imported. Preserve the source export and record historical submission import as
109
+ unsupported with an owner action until a dedicated import contract exists.
110
+
111
+ ## Pagination And Batching
112
+
113
+ The data-plane commands are paginated and capped. Read the limits instead of
114
+ assuming one request covers everything:
115
+
116
+ - `fimo entries list` and `fimo assets list` return 20 items by default and at
117
+ most 100 per request (`--limit`, `--offset`). The response carries `total`
118
+ and `hasMore`; page until `hasMore` is false before treating a listing as
119
+ complete.
120
+ - `fimo entries bulk-create` chunks at 100 entries by default. Migration data
121
+ workers use `--upsert-by slug`; the identity includes locale, the command
122
+ rereads after an ambiguous 504 before retrying absent entries, and its
123
+ receipt aggregates per-chunk created/updated/conflict counts. Use
124
+ `--chunk-size` only when the transport supports a different bounded size
125
+ (maximum 1,000). `bulk-update` and `bulk-delete` remain capped at 1,000.
126
+ - `fimo labels set-many` sends its whole `--file`/`--data` payload in one
127
+ request; keep one payload per worker batch so a retry replays a bounded set.
128
+
129
+ Listing existing Fimo entries or assets serves idempotency, retry, and fan-in
130
+ reconciliation only - confirming what a rerun must update instead of create.
131
+ The source of the content is always the source tree or export, never a listing
132
+ of what already exists in Fimo.
133
+
88
134
  ## Minimum Import Loop
89
135
 
90
136
  For each migration, agents should run this loop:
91
137
 
92
- 1. Build or refresh the canonical manifest.
93
- 2. Fill missing media, CMS, and form discovery data.
94
- 3. Generate reviewed schemas/forms/import payloads.
95
- 4. Import media first.
96
- 5. Push schemas.
97
- 6. Import entries with media references resolved.
98
- 7. Push forms.
99
- 8. Refresh the canonical manifest with target IDs and counts.
100
- 9. Write the migration report.
138
+ 1. Read the text migration plan and the initialized target.
139
+ 2. Have the owning workers define complete schemas/forms and write them through
140
+ the normal Fimo contracts.
141
+ 3. Validate and push schemas before dependent content writes.
142
+ 4. Upload media.
143
+ 5. Import entries, then resolve proven references against stable target IDs.
144
+ 6. Update singles and apply labels.
145
+ 7. Handle form definitions through their separate supported contract; do not
146
+ import historical submissions.
147
+ 8. Write target IDs, counts, failures, and unresolved items into worker and
148
+ migration reports.
149
+
150
+ The data-plane dependency order remains `schemas`, `media`,
151
+ `entries/references`, `singles`, and `labels`. It is a simple execution order,
152
+ not a reason to add another orchestrator.
101
153
 
102
154
  If any step cannot be completed, write an unresolved item immediately with the
103
155
  source evidence and owner action needed.
@@ -1,164 +1,14 @@
1
- # Canonical Migration Manifest
1
+ # Legacy migration manifest
2
2
 
3
- Write `.fimo/migration/manifest.json`. It is the source of truth for humans,
4
- follow-up agents, and import/QA checks.
3
+ New migrations do not create a canonical `manifest.json`. The coordinator uses
4
+ the readable `.fimo/migration/migration-plan.md` and
5
+ `.fimo/migration/migration-report.md` instead.
5
6
 
6
- ## Shape
7
+ Older URL-only migrations may contain `.fimo/migrations/current/manifest.json`.
8
+ Read it as source evidence when useful, but do not copy it into another
9
+ registry or ask workers to keep it synchronized.
7
10
 
8
- ```json
9
- {
10
- "version": 1,
11
- "source": {
12
- "mode": "url-only|source-backed|hybrid",
13
- "platform": "webflow|framer|squarespace|wix|wordpress|carrd|custom|unknown",
14
- "publicUrl": "https://example.com",
15
- "rootPath": "../source",
16
- "inventoryPath": ".fimo/migration/inventory.json",
17
- "sourceLedgers": []
18
- },
19
- "artifacts": {
20
- "code": [],
21
- "cms": [],
22
- "media": [],
23
- "forms": [],
24
- "screenshots": [],
25
- "other": []
26
- },
27
- "routes": [],
28
- "contentTypes": [],
29
- "entries": [],
30
- "media": [],
31
- "forms": [],
32
- "redirects": [],
33
- "locales": [],
34
- "seo": [],
35
- "unsupported": [],
36
- "unresolved": [],
37
- "qa": {
38
- "status": "not-run|passed|failed|passed-with-unresolved",
39
- "checks": []
40
- }
41
- }
42
- ```
43
-
44
- ## Required Item Fields
45
-
46
- Every route, content type, entry batch, media asset, form, redirect, locale, SEO
47
- item, unsupported item, and unresolved item should include:
48
-
49
- ```json
50
- {
51
- "id": "stable-human-readable-id",
52
- "status": "discovered|planned|generated|imported|verified|ignored|unsupported|unresolved",
53
- "sourceFiles": [],
54
- "sourceUrls": [],
55
- "targetFiles": [],
56
- "confidence": 1,
57
- "notes": []
58
- }
59
- ```
60
-
61
- Use honest confidence. Inferred content from crawled pages is lower confidence
62
- than first-party CSV/XML/JSON exports.
63
-
64
- ## Content Type Item
65
-
66
- ```json
67
- {
68
- "id": "blog-post",
69
- "uid": "BlogPost",
70
- "name": "Blog Posts",
71
- "status": "planned",
72
- "sourceFiles": ["exports/blog.csv"],
73
- "targetFiles": ["src/schemas/BlogPost.json"],
74
- "sourceCount": 42,
75
- "plannedCount": 42,
76
- "importedCount": 0,
77
- "fields": {
78
- "title": { "type": "string", "source": "Name" },
79
- "body": { "type": "richtext", "source": "Post Body" },
80
- "coverImage": { "type": "media", "source": "Main Image" },
81
- "publishedAt": { "type": "datetime", "source": "Published On" }
82
- },
83
- "references": []
84
- }
85
- ```
86
-
87
- > **Do not put reserved names in `fields`.** `id`, `documentId`, `slug`, `locale`,
88
- > `createdAt`, `updatedAt`, and `__fimo` are built-in entry metadata — declaring them as
89
- > schema fields makes `fimo schemas push` fail with a 400. Map a source "Slug" column onto
90
- > the entry's built-in `slug` (inside `data`) at import time, not onto a schema field.
91
-
92
- ## Entry Batch Item
93
-
94
- ```json
95
- {
96
- "id": "blog-post-entries",
97
- "contentType": "BlogPost",
98
- "status": "generated",
99
- "sourceFiles": ["exports/blog.csv"],
100
- "targetFiles": [".fimo/migration/entries/BlogPost.json"],
101
- "sourceCount": 42,
102
- "generatedCount": 42,
103
- "importedCount": 0,
104
- "skipped": []
105
- }
106
- ```
107
-
108
- ## Media Item
109
-
110
- ```json
111
- {
112
- "id": "media-hero-jpg",
113
- "status": "imported",
114
- "sourceUrl": "https://example.com/hero.jpg",
115
- "sourcePath": "assets/hero.jpg",
116
- "targetAssetId": "asset_123",
117
- "targetUrl": "https://...",
118
- "sha256": "optional",
119
- "contentType": "image/jpeg",
120
- "bytes": 123456,
121
- "width": 1600,
122
- "height": 900,
123
- "alt": "Hero image",
124
- "referencedBy": ["route:/", "entry:BlogPost/welcome"],
125
- "uploadError": null
126
- }
127
- ```
128
-
129
- ## Form Item
130
-
131
- ```json
132
- {
133
- "id": "contact",
134
- "name": "contact",
135
- "status": "generated",
136
- "classification": "fimo-native|third-party|unsupported",
137
- "sourceFiles": ["contact.html"],
138
- "targetFiles": ["src/forms/contact.json"],
139
- "action": "https://formspree.io/...",
140
- "method": "post",
141
- "fields": [],
142
- "unsupportedReason": null
143
- }
144
- ```
145
-
146
- ## Unresolved Item
147
-
148
- ```json
149
- {
150
- "id": "mailchimp-newsletter-action",
151
- "area": "forms",
152
- "severity": "medium",
153
- "status": "unresolved",
154
- "message": "Newsletter form posts to Mailchimp and requires integration credentials.",
155
- "suggestedAction": "Create a Fimo form or configure a Mailchimp integration with the owner."
156
- }
157
- ```
158
-
159
- ## Rules
160
-
161
- - Prefer stable source IDs over generated IDs.
162
- - Preserve original field names in `source`; use clean Fimo field IDs in schemas.
163
- - Never delete unresolved items just to make the report cleaner.
164
- - Update item statuses as work progresses.
11
+ JSON remains appropriate for actual Fimo artifacts: schema files and the
12
+ content payloads passed to data-plane commands. It is not needed to describe
13
+ worker ownership, target paths, decisions, timings, or QA status. Put those in
14
+ plain-text plans, worker reports, and the final migration report.
@@ -29,6 +29,22 @@ Record what is available:
29
29
  - sha256 or another stable hash when possible
30
30
  - source references and target references
31
31
 
32
+ ## Alt Text Belongs To The Media
33
+
34
+ Alt text is media metadata, not a content field. An image that has alt text in
35
+ the source never justifies a standalone alt field in a collection or content
36
+ schema; the alt travels with the media value.
37
+
38
+ - Preserve the source alt on the media value alongside `source` and `url`, and
39
+ pass it through the asset upload metadata when the CLI exposes it. When the
40
+ upload command cannot carry it, keep it on the media value and record the
41
+ limitation as a note on the media item.
42
+ - Never use the filename as alt text.
43
+ - When the source has no alt, a meaningful generated alt is an explicit
44
+ fallback only: mark it with the media item's `altSource`
45
+ (`references/manifest.md`) so an editor can see which alt text the source did
46
+ not provide.
47
+
32
48
  ## Dedupe
33
49
 
34
50
  Deduplicate by hash when local bytes are available. If not, deduplicate by
@@ -45,7 +61,8 @@ failing. Either upload it (and record the Fimo target), or mark it `unresolved`
45
61
  with an owner action. "Bundled locally, upload later" is not an acceptable
46
62
  terminal state for referenced media.
47
63
 
48
- After upload, update the manifest with the Fimo target asset ID/URL.
64
+ After upload, record the Fimo target asset ID/URL in the worker and migration
65
+ reports.
49
66
 
50
67
  When using downloaded files from a Fimo migration pack, copy the recorded
51
68
  `localPath` from the media map exactly. Do not reconstruct local filenames from
@@ -62,6 +79,11 @@ Replace source references in this order:
62
79
  Schema media should render through Fimo media primitives. Do not hardcode source
63
80
  URLs for editable media fields.
64
81
 
82
+ The library asset and the media value stored in an entry are distinct concerns:
83
+ the asset owns the uploaded bytes and their metadata, and the entry field holds
84
+ the media value `{ source, url, alt }` that points at it. Keep both carrying the
85
+ same alt text, and do not duplicate alt into a second schema field.
86
+
65
87
  ## Failure Handling
66
88
 
67
89
  If a media asset cannot be downloaded or uploaded:
@@ -71,3 +93,13 @@ If a media asset cannot be downloaded or uploaded:
71
93
  - list every page/entry that references it
72
94
  - include a suggested action such as re-exporting the asset folder or providing
73
95
  credentials
96
+
97
+ ## Planner obligations
98
+
99
+ The content planner enumerates every media obligation before fan-out: route and
100
+ SEO media, every schema media field, and every image node nested in richtext or
101
+ Tiptap JSON. Record its source location, consuming route/entry, and intended
102
+ target field or node. Give each obligation a stable identity that includes its
103
+ field or rich-text node path; a shared label or source URL is not unique. The
104
+ asset worker cannot report complete until every obligation has an uploaded or
105
+ duplicate Fimo mapping, or an explicit unresolved/unsupported disposition.
@@ -50,5 +50,29 @@ Use these notes to decide what is source of truth and what must be rebuilt.
50
50
 
51
51
  - Search for structured data before assuming static content: CSV, JSON, MD/MDX,
52
52
  frontmatter, CMS SDK calls, route loaders, API fixtures.
53
- - Identify the current framework only to understand routing/data, not to copy
54
- implementation patterns into Fimo.
53
+ - Identify the current framework to understand routing and data. A source
54
+ repository keeps its framework, router, and runtime for the first migration.
55
+ If detection or hosting compatibility stays unclear, stop and ask. Do not
56
+ select Next.js as a fallback; a conversion happens only after the user's
57
+ explicit choice, through the framework conversion worker and gate in
58
+ `references/workers.md`.
59
+
60
+ ## Provider-specific hosting
61
+
62
+ - A provider file is evidence, not disposable scaffolding. Inventory its rules
63
+ and classify each one as preserved, replaced, safely unnecessary
64
+ implementation detail, or unresolved.
65
+ - Preserve the file until its externally observable behavior has an accepted,
66
+ verified replacement. Delete it only when redundancy is proven or the user
67
+ explicitly accepts the loss.
68
+ - Cloudflare cache storage, purge, and revalidation code may be safely
69
+ unnecessary when it has no external caller and no accepted freshness
70
+ contract depends on it. Do not copy provider mechanics merely to imitate the
71
+ implementation.
72
+ - Redirects, security headers, route and 404 behavior, Markdown negotiation,
73
+ feeds, sitemap, `llms.txt`, social metadata, and forms are public behavior.
74
+ Preserve them or replace them with an equivalent and verify the result.
75
+ - A React Router/Vite site can remain React Router/Vite even when Cloudflare-only
76
+ hosting features are conditional or unresolved. Hosting uncertainty is not
77
+ permission to convert the application; only the user's explicit choice
78
+ starts a conversion.
@@ -1,19 +1,117 @@
1
1
  # Migration QA Gate
2
2
 
3
- Run this gate before reporting completion.
3
+ Run this gate before reporting completion. The gate is read-only: it starts
4
+ only after every other worker has returned a terminal report, and it never
5
+ repairs what it finds — failures become bounded repair tasks or unresolved
6
+ items.
4
7
 
5
8
  ## Coverage
6
9
 
7
10
  - Every intended source route is implemented, ignored with a reason, or unresolved.
11
+ - The source route surface is preserved: routes were not restructured to
12
+ shrink the migration, and no group of source pages was replaced by a generic
13
+ static shell.
14
+ - Every imported schema, entry, single, and label has a consuming target route
15
+ or component. Content that exists in Fimo but is not rendered by the
16
+ application is unwired, not covered.
17
+ - Every source content item is represented in the target: a content type, a
18
+ field, a block, a reference, a label, or an explicit unresolved item.
19
+ - The editorial model is Fimo-native and not silently flattened. Structured or
20
+ repeatable editorial sections are blocks, rich bodies are richtext, and
21
+ categories, authors, related items, and repeated taxonomies are references -
22
+ not hardcoded strings, raw IDs, or opaque JSON. Every `json` field carries a
23
+ recorded reason that no Fimo-native model fits.
24
+ - Editable shared chrome (header, footer, navigation, site settings) has a
25
+ singleton or other content model an owner can edit, and labels are limited to
26
+ small UI copy and reusable translations.
27
+ - Content type, field, and block names are semantic and editor-facing: no markup
28
+ tags, HTML attributes, locale suffixes, or column indexes.
29
+ - Modeling did not redesign the site: the migrated routes render the same
30
+ content, order, and copy as the source, and the text plan records the
31
+ source-to-Fimo mapping.
32
+ - Media alt text is attached to the media (asset metadata and media value), with
33
+ no standalone alt field in a content schema. Alt is never the filename, and a
34
+ generated fallback is recorded as generated.
35
+ - Every planner `sourceFile`/`sourceKey` maps to its original consumer. Imported
36
+ labels or prose that are only appended elsewhere, including an additive
37
+ “Page content” section, are unwired gaps. Use existing literal-key extraction
38
+ for a bounded read-only comparison when it is sound; otherwise report every
39
+ unmapped key from the planner consumer map. Do not build a generic AST or
40
+ codemod system for this check.
8
41
  - Every CMS export file is mapped to a schema, ignored with a reason, or unresolved.
9
42
  - Source entry counts match imported counts (entries created in Fimo), excluding
10
43
  documented skips/unresolved. Generated seed JSON alone does not satisfy this.
11
44
  - Every media item referenced by routes, CMS entries, SEO, or forms is uploaded to
12
45
  the Fimo media library (Fimo asset id/URL) or has an unresolved item. A local
13
46
  `public/` copy is not an upload mapping.
14
- - Every visible form has a Fimo replacement, third-party plan, or unsupported note.
47
+ - Every planner media obligation, including schema media fields and nested
48
+ richtext/Tiptap image nodes, has a mapping or explicit disposition; the asset
49
+ worker's completion claim alone is not evidence.
50
+ - Every visible form has a Fimo replacement, a preserved third-party
51
+ integration, or an unsupported note. A form became Fimo-native only when the
52
+ Fimo form matches the source behavior 1:1.
53
+ - The detected source framework, router, runtime, and configuration are
54
+ preserved. React Router/Vite remains React Router/Vite; any Next.js conversion
55
+ has the user's explicit choice. Unsupported or unclear hosting is unresolved,
56
+ not hidden by a conversion or deleted configuration.
57
+ - In conversion mode, the framework gate passed before content code work: the
58
+ conversion worker's self-checks and the coordinator's independent rerun of
59
+ the build and representative route checks are both recorded. A gate failure
60
+ caps the status at draft-only or leaves the affected routes unresolved; no
61
+ route was replaced by a generic shell or another framework as a fallback.
62
+ - Every preserved third-party integration keeps its original wiring, and the
63
+ credential environment keys it needs are recorded by name. A missing secret
64
+ is an unresolved user follow-up, never silently removed behavior.
15
65
  - SEO title, description, canonical URL, share image, slugs, dates, authors,
16
66
  categories, tags, redirects, and locales are preserved when present.
67
+ - Existing SEO, OG, and social behavior - including dynamic OG image routes -
68
+ is preserved or explicitly reported as a gap.
69
+ - When the source has no low-risk SEO or social value, safe defaults derived
70
+ from verified page/site data are present or the missing behavior is reported;
71
+ the migration must not invent marketing claims.
72
+ - Redirects and security headers stay in the source framework config when the
73
+ target keeps the source runtime and host, or are translated with a recorded
74
+ note in the text migration report when the target runtime or host requires
75
+ it.
76
+ - Every provider-specific rule is classified as preserved, replaced, safely
77
+ unnecessary implementation detail, or unresolved. A deletion has evidence of
78
+ redundancy or the user's accepted loss.
79
+ - Public behavior is verified independently: blog routes and 404s, Markdown
80
+ negotiation, RSS, sitemap, `llms.txt`, OG/social metadata, redirects,
81
+ security headers, and forms where present. Cloudflare cache storage, purge,
82
+ and revalidation internals need no copy when proven externally irrelevant.
83
+ - Each content/data lane records its source-access class. Local files/exports
84
+ and public APIs were read under their simple access rules; private sources
85
+ used a user export or narrowly scoped server-side credential. Missing access
86
+ is unresolved for that lane, and no content was guessed.
87
+
88
+ For linked Next.js targets, `withFimo` derives `FIMO_CONTENT_API_URL` and
89
+ `NEXT_PUBLIC_FIMO_API_URL` from `.fimo/project.json` during `next dev`,
90
+ `next build`, and `next start`. Both aliases resolve to the same tenant API,
91
+ while non-empty user overrides remain untouched; no committed env file is
92
+ required.
93
+
94
+ ## Hosting
95
+
96
+ Owned by the runtime lane; verified here read-only.
97
+
98
+ - The target starts locally: the dev server responds without startup errors.
99
+ - The target is embeddable in Fimo Preview, which frames the app in an iframe
100
+ and drives it over the postMessage preview bridge. Development/preview
101
+ responses carry no unconditional `X-Frame-Options` or CSP `frame-ancestors`
102
+ block; protective headers (nosniff, HSTS, referrer policy) stay. The
103
+ translation goes through `FIMO_PREVIEW_ALLOWED_ORIGINS`; `withFimo` alone
104
+ does not solve headers — it only extends `allowedDevOrigins` during
105
+ `next dev`.
106
+ - The runtime report covers local startup, preview embedding, build, derived
107
+ aliases, header translations, and integration env-key names, with every
108
+ claim backed by evidence.
109
+ - With a browser available, record `browserProof` (rendered route plus console
110
+ state) for startup and embedding. Without one, use deterministic HTTP
111
+ status/header/process/log checks and mark visual proof explicitly
112
+ unavailable in the report.
113
+ - Deployment smoke is explicit owner opt-in; never run deploy or publish as a
114
+ QA step.
17
115
 
18
116
  ## Behavior
19
117
 
@@ -21,13 +119,49 @@ Run this gate before reporting completion.
21
119
  - The project builds successfully.
22
120
  - Local dev starts without Vite loader/schema/form generation errors.
23
121
  - Primary routes render without console/runtime errors.
122
+ - In conversion mode, every route in the conversion worker's route map renders
123
+ locally with the expected status (including 404) and shows its source text
124
+ landmarks, and the target has exactly one app tree.
24
125
  - Collection listing/detail pages work with imported data, seed data, or explicit
25
126
  empty states.
127
+ - Every content-code batch exercised at least one non-empty imported item
128
+ through each Server-to-Client or plain-JSON boundary it introduced; build
129
+ success over an empty query result does not pass this check.
26
130
  - Navigation and footer links point to migrated routes or documented external URLs.
131
+ - Missing integration secrets do not block these checks: build, dev, and route
132
+ render run with the integration inert, and the missing key is a recorded
133
+ follow-up.
27
134
  - Unsupported app behavior is not hidden behind broken UI.
28
135
 
136
+ ## Browser smoke (required)
137
+
138
+ Build success and HTTP 200 responses are not sufficient evidence: a page can
139
+ build, return 200, and still render blank labels or an empty collection. Run
140
+ one bounded, scriptable browser pass (Playwright, agent-browser, or
141
+ equivalent):
142
+
143
+ 1. Start the app and wait for hydration, not just the HTTP response.
144
+ 2. Open a representative set of pages: home, one collection listing, one
145
+ collection detail, one static page.
146
+ 3. Exercise one representative interaction (open the nav menu, an accordion,
147
+ or a filter) and assert its visible result.
148
+ 4. Assert expected visible text on each opened page and at least one dynamic
149
+ collection item rendered from imported Fimo data.
150
+ 5. Assert globally rendered label/button text (header/footer nav, CTAs, cookie
151
+ banner) is non-empty; blank text usually means the component is mounted
152
+ outside the label provider.
153
+ 6. Inspect the browser console and server output for runtime errors.
154
+
155
+ Keep it bounded: representative pages and one interaction, not an interactive
156
+ walk of every route or a multi-hour visual audit. The Visual section below
157
+ remains a separate spot check.
158
+
29
159
  ## Visual
30
160
 
161
+ Visual checks need a browser. When none is available, run the deterministic
162
+ Hosting checks instead and mark visual proof explicitly unavailable in the
163
+ report; never claim a visual check that did not run.
164
+
31
165
  - Compare source and migrated site at desktop and mobile sizes.
32
166
  - Prioritize layout, typography, content hierarchy, media, and brand tokens.
33
167
  - Remove generated placeholder copy from visible UI.
@@ -42,8 +176,11 @@ Record:
42
176
  - media uploaded/failed
43
177
  - forms pushed
44
178
  - redirects/locales imported or unresolved
179
+ - the exact CLI used for the run, recorded in the report's Source section (see
180
+ "CLI provenance" in `SKILL.md`)
45
181
 
46
- Counts must be written into `.fimo/migration/manifest.json`.
182
+ Counts belong in `.fimo/migration/migration-report.md` and the relevant worker
183
+ reports. Do not create a JSON QA registry just to store them.
47
184
 
48
185
  Media, entries, and forms are blocking checks. Entries generated as seed JSON but
49
186
  not created in Fimo, and media bundled under `public/` but not uploaded to the
@@ -66,15 +203,25 @@ complete | complete-with-unresolved | draft-only
66
203
 
67
204
  ## Source
68
205
 
206
+ ## Coverage by dimension
207
+
69
208
  ## Routes
70
209
 
71
- ## CMS
210
+ ## Content
72
211
 
73
212
  ## Media
74
213
 
75
214
  ## Forms
76
215
 
77
- ## SEO, Redirects, Locales
216
+ ## SEO and OG
217
+
218
+ ## Redirects and Headers
219
+
220
+ ## Integrations
221
+
222
+ ## Hosting
223
+
224
+ ## Timing
78
225
 
79
226
  ## Checks
80
227
 
@@ -83,15 +230,22 @@ complete | complete-with-unresolved | draft-only
83
230
  ## Next Actions
84
231
  ```
85
232
 
233
+ `Coverage by dimension` is one row per migration surface (routes, content,
234
+ forms, SEO/OG, redirects/headers, integrations, hosting, media, labels, and
235
+ locales) with `gate`,
236
+ `discovered`, `migrated`, `verified`, `unresolved`, `unsupported`,
237
+ `denominator`, and `coverage`. Never collapse it into a single score: full route
238
+ coverage must not average away a missing entry or an unuploaded asset. Timing is
239
+ a separate section copied from the worker reports, so it never dilutes coverage.
240
+
241
+ Cache freshness is a report check only when the source or user made freshness an
242
+ accepted observable requirement; do not fail a migration for omitted provider
243
+ cache mechanics alone.
244
+
86
245
  Use `complete` only when all required checks pass. Use
87
246
  `complete-with-unresolved` when the migrated site is usable and all gaps are
88
247
  owner-visible. Use `draft-only` when import or verification has not happened.
89
248
 
90
- The bundled script can update `qa.status` and write the report:
91
-
92
- ```bash
93
- node <this-skill>/scripts/write-migration-report.mjs <project-root>
94
- ```
95
-
96
- If it fails, read the generated report and either complete the missing work or
97
- add precise unresolved items with owner-friendly suggested actions.
249
+ Write the report directly as text. A later evaluator may calculate the table
250
+ from it, but the migration does not need a manifest or report-generation
251
+ registry to run.