@owlmeans/viable-common 0.0.24 → 0.0.26

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 (131) hide show
  1. package/README.md +4 -4
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/agent-presentation/SKILL.md +1 -1
  4. package/agent-meta/skills/viable-common/SKILL.md +115 -13
  5. package/build/blueprint/case.d.ts.map +1 -1
  6. package/build/blueprint/case.js +58 -16
  7. package/build/blueprint/case.js.map +1 -1
  8. package/build/blueprint/resolve.d.ts.map +1 -1
  9. package/build/blueprint/resolve.js +3 -0
  10. package/build/blueprint/resolve.js.map +1 -1
  11. package/build/blueprint/types.d.ts +44 -2
  12. package/build/blueprint/types.d.ts.map +1 -1
  13. package/build/blueprint/types.js +35 -1
  14. package/build/blueprint/types.js.map +1 -1
  15. package/build/branding.d.ts +5 -1
  16. package/build/branding.d.ts.map +1 -1
  17. package/build/branding.js +6 -0
  18. package/build/branding.js.map +1 -1
  19. package/build/connect/consts.d.ts +31 -43
  20. package/build/connect/consts.d.ts.map +1 -1
  21. package/build/connect/consts.js +34 -48
  22. package/build/connect/consts.js.map +1 -1
  23. package/build/connect/entrypoints.d.ts +36 -19
  24. package/build/connect/entrypoints.d.ts.map +1 -1
  25. package/build/connect/entrypoints.js +23 -4
  26. package/build/connect/entrypoints.js.map +1 -1
  27. package/build/connect/index.d.ts +0 -1
  28. package/build/connect/index.d.ts.map +1 -1
  29. package/build/connect/index.js +0 -1
  30. package/build/connect/index.js.map +1 -1
  31. package/build/connect/ops.d.ts +1 -4
  32. package/build/connect/ops.d.ts.map +1 -1
  33. package/build/connect/references.d.ts +33 -16
  34. package/build/connect/references.d.ts.map +1 -1
  35. package/build/connect/references.js +7 -1
  36. package/build/connect/references.js.map +1 -1
  37. package/build/connect/schemas.d.ts +17 -3
  38. package/build/connect/schemas.d.ts.map +1 -1
  39. package/build/connect/schemas.js +31 -5
  40. package/build/connect/schemas.js.map +1 -1
  41. package/build/connect/types.d.ts +63 -66
  42. package/build/connect/types.d.ts.map +1 -1
  43. package/build/consts.d.ts +1 -1
  44. package/build/consts.d.ts.map +1 -1
  45. package/build/consts.js +3 -0
  46. package/build/consts.js.map +1 -1
  47. package/build/design/runtime.d.ts +1 -1
  48. package/build/design/runtime.d.ts.map +1 -1
  49. package/build/metadata/types.d.ts +8 -0
  50. package/build/metadata/types.d.ts.map +1 -1
  51. package/build/metadata/types.js.map +1 -1
  52. package/build/planning/consts.d.ts +1 -1
  53. package/build/planning/consts.js +1 -1
  54. package/build/planning/helpers.d.ts +2 -1
  55. package/build/planning/helpers.d.ts.map +1 -1
  56. package/build/planning/helpers.js +5 -2
  57. package/build/planning/helpers.js.map +1 -1
  58. package/build/planning/index.d.ts +1 -0
  59. package/build/planning/index.d.ts.map +1 -1
  60. package/build/planning/index.js +1 -0
  61. package/build/planning/index.js.map +1 -1
  62. package/build/planning/landing.d.ts +25 -0
  63. package/build/planning/landing.d.ts.map +1 -0
  64. package/build/planning/landing.js +38 -0
  65. package/build/planning/landing.js.map +1 -0
  66. package/build/planning/schemas.d.ts.map +1 -1
  67. package/build/planning/schemas.js +13 -0
  68. package/build/planning/schemas.js.map +1 -1
  69. package/build/planning/types.d.ts +28 -3
  70. package/build/planning/types.d.ts.map +1 -1
  71. package/build/scaffold/consts.d.ts +8 -0
  72. package/build/scaffold/consts.d.ts.map +1 -1
  73. package/build/scaffold/consts.js +7 -0
  74. package/build/scaffold/consts.js.map +1 -1
  75. package/build/scaffold/schemas.d.ts.map +1 -1
  76. package/build/scaffold/schemas.js +257 -12
  77. package/build/scaffold/schemas.js.map +1 -1
  78. package/build/scaffold/types.d.ts +119 -4
  79. package/build/scaffold/types.d.ts.map +1 -1
  80. package/build/skills/catalogue.d.ts.map +1 -1
  81. package/build/skills/catalogue.js +150 -39
  82. package/build/skills/catalogue.js.map +1 -1
  83. package/build/skills/consts.d.ts +5 -0
  84. package/build/skills/consts.d.ts.map +1 -1
  85. package/build/skills/consts.js +8 -0
  86. package/build/skills/consts.js.map +1 -1
  87. package/build/skills/roles.d.ts.map +1 -1
  88. package/build/skills/roles.js +11 -5
  89. package/build/skills/roles.js.map +1 -1
  90. package/build/types.d.ts +30 -0
  91. package/build/types.d.ts.map +1 -1
  92. package/package.json +8 -8
  93. package/src/blueprint/case.ts +64 -16
  94. package/src/blueprint/resolve.ts +3 -0
  95. package/src/blueprint/types.ts +57 -1
  96. package/src/branding.ts +6 -0
  97. package/src/connect/consts.ts +32 -45
  98. package/src/connect/entrypoints.ts +51 -23
  99. package/src/connect/index.ts +0 -1
  100. package/src/connect/ops.ts +1 -4
  101. package/src/connect/references.ts +48 -29
  102. package/src/connect/schemas.ts +40 -11
  103. package/src/connect/types.ts +66 -69
  104. package/src/consts.ts +3 -0
  105. package/src/design/runtime.ts +1 -1
  106. package/src/metadata/types.ts +8 -0
  107. package/src/planning/consts.ts +1 -1
  108. package/src/planning/helpers.ts +5 -2
  109. package/src/planning/index.ts +1 -0
  110. package/src/planning/landing.ts +43 -0
  111. package/src/planning/schemas.ts +13 -0
  112. package/src/planning/types.ts +29 -3
  113. package/src/scaffold/consts.ts +10 -0
  114. package/src/scaffold/schemas.ts +262 -13
  115. package/src/scaffold/types.ts +105 -5
  116. package/src/skills/catalogue.ts +151 -39
  117. package/src/skills/consts.ts +8 -0
  118. package/src/skills/roles.ts +11 -5
  119. package/src/types.ts +30 -0
  120. package/tests/blueprint.spec.ts +98 -0
  121. package/tests/branding.spec.ts +21 -1
  122. package/tests/connect-convert.spec.ts +2 -8
  123. package/tests/connect-entrypoints.spec.ts +49 -28
  124. package/tests/planning.spec.ts +56 -1
  125. package/tests/scaffold.spec.ts +131 -0
  126. package/tests/skills.spec.ts +63 -2
  127. package/build/connect/jobs.d.ts +0 -25
  128. package/build/connect/jobs.d.ts.map +0 -1
  129. package/build/connect/jobs.js +0 -29
  130. package/build/connect/jobs.js.map +0 -1
  131. package/src/connect/jobs.ts +0 -40
@@ -1,3 +1,5 @@
1
+ import type { BentoFragmentKind } from './types.js'
2
+
1
3
  /**
2
4
  * The shapes a drawn widget can take.
3
5
  *
@@ -24,3 +26,11 @@ export const AREA_HOME_WIDGET_CAP = 3
24
26
 
25
27
  /** Sections per area beyond which a top menu stops being navigable. */
26
28
  export const AREA_SECTION_CAP = 6
29
+
30
+ /**
31
+ * The shapes a landing page's bento fragment can take.
32
+ *
33
+ * A closed set for the same reason as {@link WidgetKind}: each value is one stamped primitive, so a
34
+ * value nothing can draw is a tile with an empty bottom.
35
+ */
36
+ export const BENTO_FRAGMENT_KINDS: BentoFragmentKind[] = ['list', 'note', 'people', 'steps']
@@ -1,9 +1,10 @@
1
1
  import type { JSONSchemaType } from 'ajv'
2
2
 
3
3
  import { ProjectArea } from '../areas/consts.js'
4
- import { WidgetKind } from './consts.js'
4
+ import { BENTO_FRAGMENT_KINDS, WidgetKind } from './consts.js'
5
5
  import type {
6
- GuestHomePlan, ProductIdentity, ScaffoldAreaPlan, ScaffoldPlan, ScaffoldStoryPlan, SketchContent,
6
+ BentoFragment, GuestHomePlan, LandingGatePlan, ProductIdentity,
7
+ ScaffoldAreaPlan, ScaffoldPlan, ScaffoldStoryPlan, SketchContent,
7
8
  } from './types.js'
8
9
 
9
10
  const SketchContentSchema: JSONSchemaType<SketchContent> = {
@@ -160,6 +161,166 @@ const ScaffoldAreaPlanSchema: JSONSchemaType<ScaffoldAreaPlan> = {
160
161
  additionalProperties: false,
161
162
  }
162
163
 
164
+ /**
165
+ * Every property added to the guest home after the first stored plans is OPTIONAL and `nullable`.
166
+ *
167
+ * This schema is not only what a model answers with: it is the validator of the project card's
168
+ * `scaffold` document (`VIABLE_PROJECT_SLOTS`), so a plan written before a field existed must
169
+ * still pass it whenever that plan is carried into a new revision. A field made required here is a
170
+ * project whose plan can no longer be revised.
171
+ *
172
+ * Counts ("four tiles", "5–8 chips") are stated in the descriptions and CLAMPED in code — never
173
+ * `minItems`/`maxItems`, which a provider's structured output refuses or ignores, and never a
174
+ * tuple.
175
+ */
176
+ const nullableLink = (description: string) => ({
177
+ type: 'object' as const,
178
+ nullable: true as const,
179
+ description,
180
+ properties: {
181
+ label: { type: 'string' as const, description: 'The link text, a verb first, 2-4 words' },
182
+ href: {
183
+ type: 'string' as const, nullable: true as const,
184
+ description: 'OPTIONAL, normally omitted: absent means the default action (sign in, or a'
185
+ + ' section of this page). Never invent an address'
186
+ },
187
+ },
188
+ required: ['label'] as 'label'[],
189
+ additionalProperties: false,
190
+ })
191
+
192
+ const BentoFragmentSchema: JSONSchemaType<BentoFragment> = {
193
+ type: 'object',
194
+ description: 'A small piece of the product\'s own interface drawn at the bottom of the tile, built'
195
+ + ' from its own records - never a paragraph',
196
+ properties: {
197
+ kind: {
198
+ type: 'string',
199
+ enum: BENTO_FRAGMENT_KINDS,
200
+ description: 'list (label + value rows, e.g. ingredients and amounts), note (one short remark'
201
+ + ' in the label), people (name in the label, role in the value), steps (numbered labels)'
202
+ },
203
+ title: {
204
+ type: 'string', nullable: true,
205
+ description: 'OPTIONAL small caption over the fragment, e.g. "INGREDIENTS · 1 LOAF"'
206
+ },
207
+ rows: {
208
+ type: 'array',
209
+ items: {
210
+ type: 'object',
211
+ properties: {
212
+ label: { type: 'string', description: 'The row\'s main text, a few words' },
213
+ value: {
214
+ type: 'string', nullable: true,
215
+ description: 'The row\'s second column - an amount, a role; omit it for note and steps'
216
+ },
217
+ },
218
+ required: ['label'],
219
+ additionalProperties: false,
220
+ },
221
+ description: '2-4 rows of realistic records of this product'
222
+ },
223
+ },
224
+ required: ['kind', 'rows'],
225
+ additionalProperties: false,
226
+ }
227
+
228
+ const LandingGatePlanSchema: JSONSchemaType<LandingGatePlan> = {
229
+ type: 'object',
230
+ title: 'LandingGatePlan',
231
+ description: 'The working entry into the key end-user workflow, drawn in the hero instead of the'
232
+ + ' call-to-action buttons: a guest makes a few picks, sees sample results ranked against them,'
233
+ + ' and signs in to continue with the picks carried over. Every text is in the end user\'s words',
234
+ properties: {
235
+ story: {
236
+ type: 'string',
237
+ description: 'The code of the landing story, copied EXACTLY as given to you. Never shown'
238
+ },
239
+ target: {
240
+ type: 'string', nullable: true,
241
+ description: 'Leave it out - the platform fills it'
242
+ },
243
+ question: {
244
+ type: 'string',
245
+ description: 'The question the gate asks, 3-6 words, in the end user\'s words - e.g. "What\'s'
246
+ + ' in your pantry?"'
247
+ },
248
+ hint: { type: 'string', description: 'A short reassurance beside it, e.g. "No account needed"' },
249
+ label: {
250
+ type: 'string',
251
+ description: 'The accessible name of the group of choices, 2-3 words - e.g. "Your pantry"'
252
+ },
253
+ inputs: {
254
+ type: 'array',
255
+ items: { type: 'string' },
256
+ description: '5 to 8 things a guest can pick, 1-3 words each, from the story\'s own domain.'
257
+ + ' Nothing personal, nothing sensitive, nothing that needs an account'
258
+ },
259
+ selected: {
260
+ type: 'array',
261
+ items: { type: 'string' },
262
+ description: '4 or 5 of the inputs above, copied exactly, pre-selected so the first view'
263
+ + ' already shows results'
264
+ },
265
+ results: {
266
+ type: 'array',
267
+ items: {
268
+ type: 'object',
269
+ properties: {
270
+ title: { type: 'string', description: 'A realistic record title, 2-4 words' },
271
+ meta: {
272
+ type: 'string',
273
+ description: 'One short line: how well it fits and one fact, e.g. "Uses all 3 · 3 h 20 min"'
274
+ },
275
+ needs: {
276
+ type: 'array',
277
+ items: { type: 'string' },
278
+ description: '2-4 of the inputs above, copied exactly, that this record uses'
279
+ },
280
+ },
281
+ required: ['title', 'meta', 'needs'],
282
+ additionalProperties: false,
283
+ },
284
+ description: '3 or 4 sample records the picks are ranked against - illustrative, never real data'
285
+ },
286
+ count: {
287
+ type: 'object',
288
+ properties: {
289
+ one: { type: 'string', description: 'The count for one match, with {n}: "{n} bake matches"' },
290
+ many: { type: 'string', description: 'The count for several, with {n}: "{n} bakes match"' },
291
+ none: { type: 'string', description: 'The count for none, e.g. "No matches yet"' },
292
+ },
293
+ required: ['one', 'many', 'none'],
294
+ additionalProperties: false,
295
+ description: 'The live count label; write the literal token {n} where the number goes'
296
+ },
297
+ empty: {
298
+ type: 'string',
299
+ description: 'One line shown when nothing matches, saying what to pick - e.g. "Pick two or more'
300
+ + ' ingredients to see what you can bake."'
301
+ },
302
+ note: {
303
+ type: 'string',
304
+ description: 'One short line beside the button: signing in is by email and the picks come'
305
+ + ' along - e.g. "Sign in with your email. Your picks come with you."'
306
+ },
307
+ cta: {
308
+ type: 'string',
309
+ description: 'The button label, a verb first, 2-3 words and an arrow - e.g. "Open recipes →"'
310
+ },
311
+ lock: {
312
+ type: 'string',
313
+ description: 'What unlocks after sign-in, as the accessible name of a lock icon - e.g. "Full'
314
+ + ' method after sign-in"'
315
+ },
316
+ },
317
+ required: [
318
+ 'story', 'question', 'hint', 'label', 'inputs', 'selected', 'results', 'count', 'empty',
319
+ 'note', 'cta', 'lock',
320
+ ],
321
+ additionalProperties: false,
322
+ }
323
+
163
324
  const GuestHomePlanSchema: JSONSchemaType<GuestHomePlan> = {
164
325
  type: 'object',
165
326
  title: 'GuestHomePlan',
@@ -170,16 +331,27 @@ const GuestHomePlanSchema: JSONSchemaType<GuestHomePlan> = {
170
331
  properties: {
171
332
  headline: {
172
333
  type: 'string',
173
- description: 'The product\'s promise in one line, under 9 words. Never the product name alone'
334
+ description: 'The product\'s promise in one short concrete line, at most 6 words. Never the'
335
+ + ' product name alone'
336
+ },
337
+ sub: {
338
+ type: 'string',
339
+ description: 'One or two sentences: what it does and who it is for. No invented numbers'
340
+ },
341
+ cta: {
342
+ type: 'string',
343
+ description: 'The primary button label, a verb first, 2-3 words. Shown only when the page'
344
+ + ' has no gate'
174
345
  },
175
- sub: { type: 'string', description: 'One or two sentences: what it does and who it is for' },
176
- cta: { type: 'string', description: 'The call-to-action label, 2-3 words' },
177
346
  eyebrow: {
178
347
  type: 'string', nullable: true,
179
- description: 'OPTIONAL, normally omitted. A small badge above the headline - set it only when'
180
- + ' the specification explicitly asks for a label there. A landing page carries no badge by'
181
- + ' default: the headline is what positions the product'
348
+ description: 'Omit it. The page shows no badge above the headline; the headline is what'
349
+ + ' positions the product'
182
350
  },
351
+ secondary: nullableLink('OPTIONAL second button beside the primary one, when there is no'
352
+ + ' gate - e.g. "Browse bakes"'),
353
+ browse: nullableLink('OPTIONAL muted link under the gate or the buttons for someone not'
354
+ + ' ready to start - the whole line, e.g. "Just browsing? Explore bakes"'),
183
355
  },
184
356
  required: ['headline', 'sub', 'cta'],
185
357
  additionalProperties: false,
@@ -213,11 +385,13 @@ const GuestHomePlanSchema: JSONSchemaType<GuestHomePlan> = {
213
385
  properties: {
214
386
  title: { type: 'string', description: 'The capability, 2-4 words' },
215
387
  text: { type: 'string', description: 'One sentence on what it lets someone do' },
388
+ fragment: { ...BentoFragmentSchema, nullable: true },
216
389
  },
217
390
  required: ['title', 'text'],
218
391
  additionalProperties: false,
219
392
  },
220
- description: '3 to 6 capabilities, taken from what the specification actually describes'
393
+ description: '4 capabilities - the bento tiles "What you get" - taken from what the'
394
+ + ' specification actually describes. Give most of them a fragment'
221
395
  },
222
396
  testimonials: {
223
397
  type: 'array',
@@ -231,7 +405,80 @@ const GuestHomePlanSchema: JSONSchemaType<GuestHomePlan> = {
231
405
  required: ['quote', 'name', 'role'],
232
406
  additionalProperties: false,
233
407
  },
234
- description: '2 or 3 illustrative testimonials'
408
+ description: '2 illustrative testimonials. No ratings, no numbers, no company names'
409
+ },
410
+ steps: {
411
+ type: 'array', nullable: true,
412
+ items: {
413
+ type: 'object',
414
+ properties: {
415
+ title: { type: 'string', description: 'The step as an action, 2-5 words' },
416
+ text: { type: 'string', description: 'One short sentence on what happens in it' },
417
+ },
418
+ required: ['title', 'text'],
419
+ additionalProperties: false,
420
+ },
421
+ description: '"How it works": 3 steps taken from the main flow, in order, the last one'
422
+ + ' delivering the value'
423
+ },
424
+ labels: {
425
+ type: 'object', nullable: true,
426
+ description: 'The small label over each section and that section\'s heading, in the product\'s'
427
+ + ' language. Labels are 1-3 plain words; headings are one short concrete sentence',
428
+ properties: {
429
+ steps: { type: 'string', nullable: true, description: 'e.g. "How it works"' },
430
+ stepsTitle: {
431
+ type: 'string', nullable: true,
432
+ description: 'e.g. "From pantry to proofing basket in three steps."'
433
+ },
434
+ features: { type: 'string', nullable: true, description: 'e.g. "What you get"' },
435
+ featuresTitle: {
436
+ type: 'string', nullable: true, description: 'e.g. "Everything behind the bake."'
437
+ },
438
+ testimonials: { type: 'string', nullable: true, description: 'e.g. "Community"' },
439
+ testimonialsTitle: {
440
+ type: 'string', nullable: true, description: 'e.g. "Bakers who made it at home."'
441
+ },
442
+ problem: { type: 'string', nullable: true, description: 'e.g. "The problem"' },
443
+ solution: { type: 'string', nullable: true, description: 'e.g. "The solution"' },
444
+ },
445
+ required: [],
446
+ additionalProperties: false,
447
+ },
448
+ closing: {
449
+ type: 'object', nullable: true,
450
+ description: 'The closing band at the foot of the page',
451
+ properties: {
452
+ headline: { type: 'string', description: 'A short invitation, at most 6 words' },
453
+ lead: { type: 'string', description: 'One or two sentences under it' },
454
+ primary: { type: 'string', description: 'The primary button label, a verb first, 2-4 words' },
455
+ secondary: nullableLink('OPTIONAL second button, e.g. "Browse bakes"'),
456
+ links: {
457
+ type: 'array', nullable: true,
458
+ items: {
459
+ type: 'object',
460
+ properties: {
461
+ title: { type: 'string', description: 'What the card leads to, 2-3 words' },
462
+ text: { type: 'string', description: 'One short line about it' },
463
+ href: {
464
+ type: 'string', nullable: true,
465
+ description: 'OPTIONAL, normally omitted. Never invent an address'
466
+ },
467
+ },
468
+ required: ['title', 'text'],
469
+ additionalProperties: false,
470
+ },
471
+ description: '2 link cards beside the closing headline'
472
+ },
473
+ },
474
+ required: ['headline', 'lead', 'primary'],
475
+ additionalProperties: false,
476
+ },
477
+ gate: {
478
+ ...LandingGatePlanSchema,
479
+ nullable: true,
480
+ description: 'ONLY when you were given a landing story; omit it otherwise. '
481
+ + LandingGatePlanSchema.description,
235
482
  },
236
483
  },
237
484
  required: ['hero', 'problem', 'solution', 'features', 'testimonials'],
@@ -278,9 +525,11 @@ export const ScaffoldPlanSchema: JSONSchemaType<ScaffoldPlan> = {
278
525
  },
279
526
  motifs: {
280
527
  type: 'string',
281
- description: 'Two or three sentences directing this product\'s illustrations: what shapes'
282
- + ' and subjects belong to it, in the design system\'s own vocabulary. Abstract geometry,'
283
- + ' never a described photograph'
528
+ description: 'Two or three sentences describing this product\'s hero illustration as a SCENE:'
529
+ + ' one main object of its domain resting on a surface, one or two smaller objects beside'
530
+ + ' it, and one simplified piece of the product\'s interface, each in its natural muted'
531
+ + ' colours. Concrete recognisable objects - never abstract geometry, never floating'
532
+ + ' circles, rings or dots, never people, text or a described photograph'
284
533
  },
285
534
  },
286
535
  required: ['identity', 'guestHome', 'areas', 'stories', 'motifs'],
@@ -89,22 +89,122 @@ export interface ProductIdentity {
89
89
  description: string
90
90
  }
91
91
 
92
+ /**
93
+ * A labelled link on the landing page — a secondary pill, the "just browsing" link, a closing card.
94
+ *
95
+ * `href` is OPTIONAL and normally absent: absent means the default action the stamper decides
96
+ * (start sign-in, or jump to a section of the page itself). A model never knows the application's
97
+ * addresses, and an invented one is a link that 404s on the public face of the product.
98
+ */
99
+ export interface PlanLink {
100
+ label: string
101
+ href?: string
102
+ }
103
+
104
+ /** The four shapes a bento tile's product fragment can take — each one a stamped primitive. */
105
+ export type BentoFragmentKind = 'list' | 'note' | 'people' | 'steps'
106
+
107
+ /**
108
+ * A small piece of the product's own interface, drawn at the bottom of a bento tile.
109
+ *
110
+ * Structured for the same reason {@link SketchContent} is: the fragment is STAMPED, so it must be
111
+ * rows a component can lay out, never a paragraph describing them. Built from the product's own
112
+ * records, and decorative — the stamper renders it `aria-hidden`.
113
+ */
114
+ export interface BentoFragment {
115
+ kind: BentoFragmentKind
116
+ /** Small caption over the fragment ("INGREDIENTS · 1 LOAF", "Baker's note"). */
117
+ title?: string
118
+ /** list: {label, value}; note: {label: text}; people: {label: name, value: role}; steps: {label}. */
119
+ rows: { label: string, value?: string }[]
120
+ }
121
+
122
+ /**
123
+ * The landing gate — the working entry into the product's key END-USER workflow, drawn in the
124
+ * hero where the call-to-action pills would otherwise be.
125
+ *
126
+ * A guest makes a few non-sensitive choices on the landing page, sees sample results ranked
127
+ * against them, and signs in to continue on the story's full-scale screen with those choices
128
+ * carried over. Every field is copy or sample data in the end user's own words; nothing here is
129
+ * an address the model invented — `target` is filled by code.
130
+ */
131
+ export interface LandingGatePlan {
132
+ /** The key end-user story's code — never rendered. */
133
+ story: string
134
+ /** Entrypoint ALIAS of the story's full-scale screen in the user area (filled by code, not the model). */
135
+ target?: string
136
+ /** The question the gate asks, in the end user's words: "What's in your pantry?". */
137
+ question: string
138
+ /** The reassurance beside it: "No account needed". */
139
+ hint: string
140
+ /** The accessible name of the chip group: "Your pantry". */
141
+ label: string
142
+ /** 5–8 chip labels the guest picks from. */
143
+ inputs: string[]
144
+ /** 4–5 of `inputs`, pre-selected so the first view already shows results. */
145
+ selected: string[]
146
+ /** 3–4 sample records; `needs` is the subset of `inputs` each one uses. */
147
+ results: { title: string, meta: string, needs: string[] }[]
148
+ /** The live count label; `{n}` is replaced by the number ("{n} bakes match"). */
149
+ count: { one: string, many: string, none: string }
150
+ /** The line shown when nothing matches the picks. */
151
+ empty: string
152
+ /** The line beside the CTA: "Sign in with your email. Your picks come with you.". */
153
+ note: string
154
+ /** The CTA label: "Open recipes →". */
155
+ cta: string
156
+ /** The accessible name of a result row's lock icon: "Full method after sign-in". */
157
+ lock: string
158
+ }
159
+
92
160
  /** The guest area's landing page — the product's public face. */
93
161
  export interface GuestHomePlan {
94
162
  hero: {
95
163
  headline: string
96
164
  sub: string
165
+ /** The primary pill's label — shown only when the page has no {@link GuestHomePlan.gate}. */
97
166
  cta: string
98
167
  /**
99
- * A badge above the headline — OPTIONAL and normally absent. A landing page carries no badge
100
- * unless its specification asks for one; the headline is what positions the product.
168
+ * A badge above the headline — OPTIONAL and kept only so an older stored plan still reads.
169
+ * The page renders no badge: the headline is what positions the product.
101
170
  */
102
171
  eyebrow?: string
172
+ /** The second pill beside the primary one, when there is no gate. */
173
+ secondary?: PlanLink
174
+ /** The muted text link under the gate or the pills: "Just browsing? Explore bakes ›". */
175
+ browse?: PlanLink
103
176
  }
104
177
  problem: { title: string, text: string }
105
178
  solution: { title: string, text: string }
106
- features: { title: string, text: string }[]
179
+ /** The bento tiles — four, each optionally anchored by a fragment of the product's interface. */
180
+ features: { title: string, text: string, fragment?: BentoFragment }[]
107
181
  testimonials: { quote: string, name: string, role: string }[]
182
+ /** "How it works" — three steps taken from the main flow. */
183
+ steps?: { title: string, text: string }[]
184
+ /** The small section labels and section headings; absent ones fall back to the stamper's own. */
185
+ labels?: {
186
+ steps?: string
187
+ stepsTitle?: string
188
+ features?: string
189
+ featuresTitle?: string
190
+ testimonials?: string
191
+ testimonialsTitle?: string
192
+ problem?: string
193
+ solution?: string
194
+ }
195
+ /** The closing band: a headline, a lead, the two pills and two link cards. */
196
+ closing?: {
197
+ headline: string
198
+ lead: string
199
+ primary: string
200
+ secondary?: PlanLink
201
+ links?: { title: string, text: string, href?: string }[]
202
+ }
203
+ /**
204
+ * The landing gate — present only when a gate story was decided for the project. When it is,
205
+ * it REPLACES the hero's pills.
206
+ */
207
+ gate?: LandingGatePlan
108
208
  }
109
209
 
110
210
  /**
@@ -120,8 +220,8 @@ export interface ScaffoldPlan {
120
220
  areas: ScaffoldAreaPlan[]
121
221
  stories: ScaffoldStoryPlan[]
122
222
  /**
123
- * How this product's illustrations should look — geometry and motif, in the design system's
124
- * own vocabulary. The one field here that is prose, because it is read by a model.
223
+ * How this product's illustrations should look — the scene, its domain objects and their
224
+ * palette. The one field here that is prose, because it is read by a model.
125
225
  */
126
226
  motifs: string
127
227
  }