@protoyard/blueprint 0.0.0-stage → 1.1.1

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 (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1145 -2
  3. package/dist/aggregate.d.ts +83 -0
  4. package/dist/aggregate.js +268 -0
  5. package/dist/app-model.d.ts +76 -0
  6. package/dist/app-model.js +287 -0
  7. package/dist/archetypes/index.d.ts +1 -0
  8. package/dist/archetypes/index.js +1 -0
  9. package/dist/archetypes/schema.d.ts +126 -0
  10. package/dist/archetypes/schema.js +77 -0
  11. package/dist/audit.d.ts +52 -0
  12. package/dist/audit.js +149 -0
  13. package/dist/authoring/deck-lint.d.ts +32 -0
  14. package/dist/authoring/deck-lint.js +567 -0
  15. package/dist/authoring/index.d.ts +1 -0
  16. package/dist/authoring/index.js +1 -0
  17. package/dist/authoring/select-deck-layouts.d.ts +50 -0
  18. package/dist/authoring/select-deck-layouts.js +213 -0
  19. package/dist/authoring-apis.d.ts +13 -0
  20. package/dist/authoring-apis.js +13 -0
  21. package/dist/authoring-validation.d.ts +65 -0
  22. package/dist/authoring-validation.js +267 -0
  23. package/dist/avatar.d.ts +25 -0
  24. package/dist/avatar.js +30 -0
  25. package/dist/brand.d.ts +70 -0
  26. package/dist/brand.js +294 -0
  27. package/dist/brief/archetypes.d.ts +28 -0
  28. package/dist/brief/archetypes.js +798 -0
  29. package/dist/brief/content-synthesize.d.ts +31 -0
  30. package/dist/brief/content-synthesize.js +210 -0
  31. package/dist/brief/deck-outlines.d.ts +96 -0
  32. package/dist/brief/deck-outlines.js +561 -0
  33. package/dist/brief/draft-bodies.d.ts +29 -0
  34. package/dist/brief/draft-bodies.js +224 -0
  35. package/dist/brief/fields.d.ts +19 -0
  36. package/dist/brief/fields.js +229 -0
  37. package/dist/brief/index.d.ts +8 -0
  38. package/dist/brief/index.js +8 -0
  39. package/dist/brief/kind-detection.d.ts +69 -0
  40. package/dist/brief/kind-detection.js +353 -0
  41. package/dist/brief/limits.d.ts +117 -0
  42. package/dist/brief/limits.js +217 -0
  43. package/dist/brief/page-doc-outlines.d.ts +80 -0
  44. package/dist/brief/page-doc-outlines.js +485 -0
  45. package/dist/brief/synthesize.d.ts +59 -0
  46. package/dist/brief/synthesize.js +511 -0
  47. package/dist/brief/text.d.ts +13 -0
  48. package/dist/brief/text.js +59 -0
  49. package/dist/canvas-screen.d.ts +31 -0
  50. package/dist/canvas-screen.js +33 -0
  51. package/dist/canvas-seed.d.ts +58 -0
  52. package/dist/canvas-seed.js +82 -0
  53. package/dist/computed/evaluate.d.ts +11 -0
  54. package/dist/computed/evaluate.js +101 -0
  55. package/dist/computed/index.d.ts +4 -0
  56. package/dist/computed/index.js +3 -0
  57. package/dist/computed/public.d.ts +14 -0
  58. package/dist/computed/public.js +19 -0
  59. package/dist/computed/schema.d.ts +10 -0
  60. package/dist/computed/schema.js +140 -0
  61. package/dist/computed/types.d.ts +70 -0
  62. package/dist/computed/types.js +1 -0
  63. package/dist/computed/validate.d.ts +6 -0
  64. package/dist/computed/validate.js +146 -0
  65. package/dist/csv-guard.d.ts +15 -0
  66. package/dist/csv-guard.js +29 -0
  67. package/dist/csv.d.ts +85 -0
  68. package/dist/csv.js +288 -0
  69. package/dist/files.d.ts +35 -0
  70. package/dist/files.js +246 -0
  71. package/dist/form-schema.d.ts +124 -0
  72. package/dist/form-schema.js +300 -0
  73. package/dist/generator-version.d.ts +11 -0
  74. package/dist/generator-version.js +51 -0
  75. package/dist/generator.d.ts +34 -0
  76. package/dist/generator.js +445 -0
  77. package/dist/identity.d.ts +34 -0
  78. package/dist/identity.js +91 -0
  79. package/dist/import/csv-shape.d.ts +40 -0
  80. package/dist/import/csv-shape.js +265 -0
  81. package/dist/import/json-shape.d.ts +55 -0
  82. package/dist/import/json-shape.js +491 -0
  83. package/dist/import/limits.d.ts +13 -0
  84. package/dist/import/limits.js +14 -0
  85. package/dist/import/sqlite-schema.d.ts +36 -0
  86. package/dist/import/sqlite-schema.js +729 -0
  87. package/dist/index.d.ts +60 -0
  88. package/dist/index.js +58 -0
  89. package/dist/kinds/anchors.d.ts +18 -0
  90. package/dist/kinds/anchors.js +46 -0
  91. package/dist/kinds/bodies.d.ts +6 -0
  92. package/dist/kinds/bodies.js +32 -0
  93. package/dist/kinds/brand-runtime.d.ts +19 -0
  94. package/dist/kinds/brand-runtime.js +35 -0
  95. package/dist/kinds/deck.d.ts +8 -0
  96. package/dist/kinds/deck.js +34 -0
  97. package/dist/kinds/doc.d.ts +19 -0
  98. package/dist/kinds/doc.js +41 -0
  99. package/dist/kinds/embed/index.d.ts +39 -0
  100. package/dist/kinds/embed/index.js +91 -0
  101. package/dist/kinds/envelope.d.ts +159 -0
  102. package/dist/kinds/envelope.js +78 -0
  103. package/dist/kinds/index.d.ts +11 -0
  104. package/dist/kinds/index.js +11 -0
  105. package/dist/kinds/kind.d.ts +36 -0
  106. package/dist/kinds/kind.js +55 -0
  107. package/dist/kinds/limits.d.ts +64 -0
  108. package/dist/kinds/limits.js +190 -0
  109. package/dist/kinds/page-doc-runtime.d.ts +37 -0
  110. package/dist/kinds/page-doc-runtime.js +55 -0
  111. package/dist/kinds/page.d.ts +16 -0
  112. package/dist/kinds/page.js +67 -0
  113. package/dist/kinds/parse.d.ts +18 -0
  114. package/dist/kinds/parse.js +27 -0
  115. package/dist/kinds/runtime.d.ts +128 -0
  116. package/dist/kinds/runtime.js +206 -0
  117. package/dist/kinds/services.d.ts +49 -0
  118. package/dist/kinds/services.js +249 -0
  119. package/dist/kinds/testing.d.ts +9 -0
  120. package/dist/kinds/testing.js +16 -0
  121. package/dist/migrations.d.ts +97 -0
  122. package/dist/migrations.js +351 -0
  123. package/dist/motion.d.ts +50 -0
  124. package/dist/motion.js +52 -0
  125. package/dist/notifications.d.ts +19 -0
  126. package/dist/notifications.js +177 -0
  127. package/dist/patch.d.ts +122 -0
  128. package/dist/patch.js +322 -0
  129. package/dist/policy.d.ts +23 -0
  130. package/dist/policy.js +105 -0
  131. package/dist/runtime-definition.d.ts +39 -0
  132. package/dist/runtime-definition.js +74 -0
  133. package/dist/scenarios.d.ts +9 -0
  134. package/dist/scenarios.js +49 -0
  135. package/dist/schema/blueprint.schema.json +10191 -0
  136. package/dist/schema-json.d.ts +6 -0
  137. package/dist/schema-json.js +36 -0
  138. package/dist/schema.d.ts +6348 -0
  139. package/dist/schema.js +1269 -0
  140. package/dist/search.d.ts +16 -0
  141. package/dist/search.js +96 -0
  142. package/dist/services.d.ts +89 -0
  143. package/dist/services.js +898 -0
  144. package/dist/share.d.ts +78 -0
  145. package/dist/share.js +234 -0
  146. package/dist/sheet-screen.d.ts +49 -0
  147. package/dist/sheet-screen.js +91 -0
  148. package/dist/storage.d.ts +43 -0
  149. package/dist/storage.js +179 -0
  150. package/dist/stream.d.ts +26 -0
  151. package/dist/stream.js +173 -0
  152. package/dist/untrusted.d.ts +63 -0
  153. package/dist/untrusted.js +502 -0
  154. package/dist/validation-tasks.d.ts +178 -0
  155. package/dist/validation-tasks.js +199 -0
  156. package/dist/validation.d.ts +35 -0
  157. package/dist/validation.js +312 -0
  158. package/dist/variants.d.ts +112 -0
  159. package/dist/variants.js +283 -0
  160. package/dist/workflows/index.d.ts +3 -0
  161. package/dist/workflows/index.js +3 -0
  162. package/dist/workflows/schema.d.ts +50 -0
  163. package/dist/workflows/schema.js +34 -0
  164. package/dist/workflows/transition.d.ts +15 -0
  165. package/dist/workflows/transition.js +25 -0
  166. package/dist/workflows/validate.d.ts +17 -0
  167. package/dist/workflows/validate.js +140 -0
  168. package/package.json +120 -3
@@ -0,0 +1,80 @@
1
+ export declare const PAGE_OUTLINE_IDS: readonly ["landing", "pricing", "waitlist", "generic"];
2
+ export type PageOutlineId = (typeof PAGE_OUTLINE_IDS)[number];
3
+ export declare const DOC_OUTLINE_IDS: readonly ["report", "handbook", "one-pager", "proposal", "generic"];
4
+ export type DocOutlineId = (typeof DOC_OUTLINE_IDS)[number];
5
+ /** Draft block shapes for outlines — placeholders only, not full BlockSchema. */
6
+ export type OutlineBlock = {
7
+ type: 'heading';
8
+ text: string;
9
+ level?: 1 | 2 | 3 | 4;
10
+ } | {
11
+ type: 'text';
12
+ markdown: string;
13
+ } | {
14
+ type: 'list';
15
+ style?: 'bullet' | 'number' | 'check';
16
+ items: string[];
17
+ } | {
18
+ type: 'cta';
19
+ label: string;
20
+ href?: string;
21
+ } | {
22
+ type: 'stat';
23
+ items: {
24
+ value: string;
25
+ label: string;
26
+ }[];
27
+ } | {
28
+ type: 'quote';
29
+ quote: string;
30
+ cite?: string;
31
+ } | {
32
+ type: 'callout';
33
+ body: string;
34
+ title?: string;
35
+ } | {
36
+ type: 'divider';
37
+ label?: string;
38
+ };
39
+ export interface OutlineSection {
40
+ id: string;
41
+ role: string;
42
+ title: string;
43
+ /** First plain-text teaser (editors that only read `body`). */
44
+ body: string;
45
+ blocks: OutlineBlock[];
46
+ }
47
+ export interface PageOutlineResult {
48
+ outline: PageOutlineId;
49
+ title: string;
50
+ sections: OutlineSection[];
51
+ rationale: string[];
52
+ /** Interactive or product features the brief asked for that this outline skips. */
53
+ omissions: string[];
54
+ }
55
+ export interface DocOutlineResult {
56
+ outline: DocOutlineId;
57
+ title: string;
58
+ sections: OutlineSection[];
59
+ rationale: string[];
60
+ omissions: string[];
61
+ }
62
+ export interface OutlineBuildOptions {
63
+ /** Forced product / document title. */
64
+ title?: string;
65
+ }
66
+ /**
67
+ * Interactive or backend features a page/doc outline will not invent.
68
+ * Callers surface these in rationale so agents do not treat them as shipped.
69
+ */
70
+ export declare function detectPageDocOmissions(brief: string): string[];
71
+ /** Choose a page outline family from brief cues (deterministic). */
72
+ export declare function selectPageOutline(brief: string): PageOutlineId;
73
+ /** Choose a doc outline family from brief cues (deterministic). */
74
+ export declare function selectDocOutline(brief: string): DocOutlineId;
75
+ /** Build a deterministic page outline from a brief. */
76
+ export declare function buildPageOutline(brief: string, options?: OutlineBuildOptions): PageOutlineResult;
77
+ /** Build a deterministic doc outline from a brief. */
78
+ export declare function buildDocOutline(brief: string, options?: OutlineBuildOptions): DocOutlineResult;
79
+ /** Stable ordered section ids for assertions and lint. */
80
+ export declare function sectionIds(sections: readonly OutlineSection[]): string[];
@@ -0,0 +1,485 @@
1
+ /**
2
+ * Section-based outlines for page and doc briefs (ADR-036).
3
+ *
4
+ * Pure and deterministic: stable section ids, ordered sections, bounded block
5
+ * vocabulary (heading, text, list, cta, stat, quote, callout, divider). No app
6
+ * entities or roles. Unsupported interactive features are listed as explicit
7
+ * omissions in the rationale — never invented as live behavior.
8
+ */
9
+ import { hashString } from '@protoyard/core';
10
+ import { hasPhrase, normalize, titleCase } from "./text.js";
11
+ export const PAGE_OUTLINE_IDS = ['landing', 'pricing', 'waitlist', 'generic'];
12
+ export const DOC_OUTLINE_IDS = ['report', 'handbook', 'one-pager', 'proposal', 'generic'];
13
+ const NAME_WORDS = [
14
+ 'Harbor',
15
+ 'Lumen',
16
+ 'Cedar',
17
+ 'Beacon',
18
+ 'Juniper',
19
+ 'Copper',
20
+ 'Willow',
21
+ 'Lantern',
22
+ 'Meadow',
23
+ 'Summit',
24
+ 'Quarry',
25
+ 'Tidal',
26
+ 'Ember',
27
+ 'Orchard',
28
+ 'Kestrel',
29
+ 'Maple',
30
+ ];
31
+ function firstSentence(source) {
32
+ const sentence = source
33
+ .replace(/\s+/g, ' ')
34
+ .trim()
35
+ .split(/(?<=[.!?])\s/)[0] ?? '';
36
+ return sentence.slice(0, 280);
37
+ }
38
+ function pickTitle(source, kind, forced) {
39
+ if (forced?.trim())
40
+ return forced.trim().slice(0, 80);
41
+ const fromBrief = firstSentence(source);
42
+ if (fromBrief.length >= 8)
43
+ return fromBrief.slice(0, 80);
44
+ const word = NAME_WORDS[hashString(normalize(source) || kind) % NAME_WORDS.length];
45
+ return `${word} ${kind === 'page' ? 'Page' : 'Doc'}`.slice(0, 80);
46
+ }
47
+ function section(id, role, title, blocks) {
48
+ return {
49
+ id,
50
+ role,
51
+ title: title.slice(0, 80),
52
+ body: bodyFromBlocks(blocks),
53
+ blocks,
54
+ };
55
+ }
56
+ function bodyFromBlocks(blocks) {
57
+ for (const block of blocks) {
58
+ if (block.type === 'text')
59
+ return block.markdown.slice(0, 280);
60
+ if (block.type === 'heading')
61
+ return block.text.slice(0, 280);
62
+ if (block.type === 'callout')
63
+ return block.body.slice(0, 280);
64
+ if (block.type === 'quote')
65
+ return block.quote.slice(0, 280);
66
+ if (block.type === 'list' && block.items[0])
67
+ return block.items[0].slice(0, 280);
68
+ if (block.type === 'cta')
69
+ return block.label.slice(0, 280);
70
+ }
71
+ return 'Add content here.';
72
+ }
73
+ /**
74
+ * Interactive or backend features a page/doc outline will not invent.
75
+ * Callers surface these in rationale so agents do not treat them as shipped.
76
+ */
77
+ export function detectPageDocOmissions(brief) {
78
+ const n = normalize(brief);
79
+ const omissions = [];
80
+ const add = (line) => {
81
+ if (!omissions.includes(line))
82
+ omissions.push(line);
83
+ };
84
+ if (hasPhrase(n, 'contact form') ||
85
+ hasPhrase(n, 'signup form') ||
86
+ hasPhrase(n, 'sign up form') ||
87
+ hasPhrase(n, 'registration form') ||
88
+ hasPhrase(n, 'waitlist form') ||
89
+ (hasPhrase(n, 'form') && (hasPhrase(n, 'submit') || hasPhrase(n, 'collect')))) {
90
+ add('Interactive forms and response storage are omitted — @protoyard/pages owns form persistence.');
91
+ }
92
+ if (hasPhrase(n, 'live poll') ||
93
+ hasPhrase(n, 'audience poll') ||
94
+ hasPhrase(n, 'quiz') ||
95
+ hasPhrase(n, 'survey')) {
96
+ add('Live polls, quizzes and surveys are omitted from page/doc outlines (use deck blocks or apps).');
97
+ }
98
+ if (hasPhrase(n, 'payment') ||
99
+ hasPhrase(n, 'checkout') ||
100
+ hasPhrase(n, 'stripe') ||
101
+ hasPhrase(n, 'billing')) {
102
+ add('Payments and checkout are omitted — simulated billing stays in the SaaS kit, not page outlines.');
103
+ }
104
+ if (hasPhrase(n, 'login') ||
105
+ hasPhrase(n, 'sign in') ||
106
+ hasPhrase(n, 'sso') ||
107
+ hasPhrase(n, 'oauth') ||
108
+ hasPhrase(n, 'authentication')) {
109
+ add('Authentication and SSO are omitted — use an app or SaaS kit blueprint for sign-in flows.');
110
+ }
111
+ if (hasPhrase(n, 'real time') ||
112
+ hasPhrase(n, 'realtime') ||
113
+ hasPhrase(n, 'websocket') ||
114
+ hasPhrase(n, 'live chat')) {
115
+ add('Real-time chat and websockets are omitted — content kinds stay static drafts.');
116
+ }
117
+ if (hasPhrase(n, 'api') &&
118
+ (hasPhrase(n, 'backend') || hasPhrase(n, 'server') || hasPhrase(n, 'database'))) {
119
+ add('Backend APIs and databases are omitted — blueprints stay data-only with no network.');
120
+ }
121
+ if (hasPhrase(n, 'crm') || hasPhrase(n, 'entities') || hasPhrase(n, 'roles and permissions')) {
122
+ add('App entities and roles are omitted — page/doc kinds have no entity model.');
123
+ }
124
+ return omissions;
125
+ }
126
+ /** Choose a page outline family from brief cues (deterministic). */
127
+ export function selectPageOutline(brief) {
128
+ const n = normalize(brief);
129
+ // Landing / marketing pages win even when they also mention pricing tiers.
130
+ if (hasPhrase(n, 'landing page') ||
131
+ hasPhrase(n, 'marketing page') ||
132
+ hasPhrase(n, 'marketing site') ||
133
+ hasPhrase(n, 'product page') ||
134
+ hasPhrase(n, 'sales page') ||
135
+ hasPhrase(n, 'splash page') ||
136
+ hasPhrase(n, 'homepage') ||
137
+ hasPhrase(n, 'home page') ||
138
+ hasPhrase(n, 'microsite')) {
139
+ return 'landing';
140
+ }
141
+ if (hasPhrase(n, 'pricing page') ||
142
+ hasPhrase(n, 'plans and pricing') ||
143
+ hasPhrase(n, 'plan comparison') ||
144
+ (hasPhrase(n, 'pricing') && (hasPhrase(n, 'plans') || hasPhrase(n, 'tiers')))) {
145
+ return 'pricing';
146
+ }
147
+ if (hasPhrase(n, 'waitlist') ||
148
+ hasPhrase(n, 'coming soon') ||
149
+ hasPhrase(n, 'launch page') ||
150
+ hasPhrase(n, 'early access')) {
151
+ return 'waitlist';
152
+ }
153
+ return 'generic';
154
+ }
155
+ /** Choose a doc outline family from brief cues (deterministic). */
156
+ export function selectDocOutline(brief) {
157
+ const n = normalize(brief);
158
+ if (hasPhrase(n, 'one pager') ||
159
+ hasPhrase(n, 'one-pager') ||
160
+ hasPhrase(n, 'onepagers') ||
161
+ hasPhrase(n, 'one pagers') ||
162
+ hasPhrase(n, 'one page report') ||
163
+ hasPhrase(n, 'one-page report') ||
164
+ hasPhrase(n, 'one page summary')) {
165
+ return 'one-pager';
166
+ }
167
+ if (hasPhrase(n, 'handbook') ||
168
+ hasPhrase(n, 'playbook') ||
169
+ hasPhrase(n, 'style guide') ||
170
+ hasPhrase(n, 'employee guide')) {
171
+ return 'handbook';
172
+ }
173
+ if (hasPhrase(n, 'proposal') ||
174
+ hasPhrase(n, 'rfp') ||
175
+ hasPhrase(n, 'statement of work') ||
176
+ hasPhrase(n, 'sow')) {
177
+ return 'proposal';
178
+ }
179
+ if (hasPhrase(n, 'quarterly report') ||
180
+ hasPhrase(n, 'status report') ||
181
+ hasPhrase(n, 'annual report') ||
182
+ hasPhrase(n, 'the report') ||
183
+ hasPhrase(n, 'a report') ||
184
+ hasPhrase(n, 'write the report') ||
185
+ hasPhrase(n, 'write a report') ||
186
+ hasPhrase(n, 'report covering') ||
187
+ hasPhrase(n, 'report for') ||
188
+ hasPhrase(n, 'whitepaper') ||
189
+ hasPhrase(n, 'research brief')) {
190
+ return 'report';
191
+ }
192
+ return 'generic';
193
+ }
194
+ function placeholder(topic) {
195
+ return `Add ${topic} here. Draft scaffolding only — no fabricated facts.`;
196
+ }
197
+ function buildPageSections(outline, title) {
198
+ switch (outline) {
199
+ case 'landing':
200
+ return [
201
+ section('section-hero', 'hero', title, [
202
+ { type: 'heading', text: title, level: 1 },
203
+ { type: 'text', markdown: placeholder('the hero claim and supporting line') },
204
+ { type: 'cta', label: 'Primary action', href: '#get-started' },
205
+ ]),
206
+ section('section-features', 'features', 'Features', [
207
+ { type: 'heading', text: 'Features', level: 2 },
208
+ {
209
+ type: 'list',
210
+ style: 'bullet',
211
+ items: [
212
+ 'Feature one — replace with a real benefit',
213
+ 'Feature two — replace with a real benefit',
214
+ 'Feature three — replace with a real benefit',
215
+ ],
216
+ },
217
+ ]),
218
+ section('section-social', 'social-proof', 'What people say', [
219
+ { type: 'heading', text: 'What people say', level: 2 },
220
+ { type: 'quote', quote: 'Add a short testimonial.', cite: 'Example customer' },
221
+ ]),
222
+ section('section-cta', 'cta', 'Get started', [
223
+ { type: 'heading', text: 'Get started', level: 2 },
224
+ { type: 'text', markdown: placeholder('a closing invite') },
225
+ { type: 'cta', label: 'Get started', href: '#get-started' },
226
+ ]),
227
+ section('section-footer', 'footer', 'Footer', [
228
+ { type: 'divider', label: 'Footer' },
229
+ { type: 'text', markdown: placeholder('contact or legal links') },
230
+ ]),
231
+ ];
232
+ case 'pricing':
233
+ return [
234
+ section('section-hero', 'hero', title, [
235
+ { type: 'heading', text: title, level: 1 },
236
+ { type: 'text', markdown: placeholder('who the plans are for') },
237
+ ]),
238
+ section('section-plans', 'pricing', 'Plans', [
239
+ { type: 'heading', text: 'Plans', level: 2 },
240
+ {
241
+ type: 'list',
242
+ style: 'bullet',
243
+ items: [
244
+ 'Starter — placeholder tier',
245
+ 'Team — placeholder tier',
246
+ 'Business — placeholder tier',
247
+ ],
248
+ },
249
+ {
250
+ type: 'callout',
251
+ title: 'Pricing is draft',
252
+ body: 'Replace tiers with real numbers before publish. Nothing here is billed.',
253
+ },
254
+ ]),
255
+ section('section-compare', 'comparison', 'Compare', [
256
+ { type: 'heading', text: 'Compare', level: 2 },
257
+ { type: 'text', markdown: placeholder('a short comparison of plan limits') },
258
+ ]),
259
+ section('section-cta', 'cta', 'Choose a plan', [
260
+ { type: 'cta', label: 'Choose a plan', href: '#plans' },
261
+ ]),
262
+ section('section-footer', 'footer', 'Footer', [
263
+ { type: 'text', markdown: placeholder('contact or FAQ link') },
264
+ ]),
265
+ ];
266
+ case 'waitlist':
267
+ return [
268
+ section('section-hero', 'hero', title, [
269
+ { type: 'heading', text: title, level: 1 },
270
+ { type: 'text', markdown: placeholder('what is coming and when') },
271
+ { type: 'cta', label: 'Join the waitlist', href: '#waitlist' },
272
+ ]),
273
+ section('section-why', 'body', 'Why it matters', [
274
+ { type: 'heading', text: 'Why it matters', level: 2 },
275
+ { type: 'list', style: 'bullet', items: ['Benefit one', 'Benefit two', 'Benefit three'] },
276
+ ]),
277
+ section('section-cta', 'cta', 'Stay in the loop', [
278
+ {
279
+ type: 'callout',
280
+ title: 'Waitlist capture',
281
+ body: 'Form fields are omitted here; wire a pages form when you need responses.',
282
+ },
283
+ { type: 'cta', label: 'Notify me', href: '#waitlist' },
284
+ ]),
285
+ section('section-footer', 'footer', 'Footer', [
286
+ { type: 'text', markdown: placeholder('contact') },
287
+ ]),
288
+ ];
289
+ default:
290
+ return [
291
+ section('section-hero', 'hero', title, [
292
+ { type: 'heading', text: title, level: 1 },
293
+ { type: 'text', markdown: 'Hero placeholder.' },
294
+ ]),
295
+ section('section-body', 'body', 'Details', [
296
+ { type: 'heading', text: 'Details', level: 2 },
297
+ { type: 'text', markdown: 'Add the main page sections here.' },
298
+ ]),
299
+ section('section-footer', 'footer', 'Footer', [
300
+ { type: 'text', markdown: 'Contact or links placeholder.' },
301
+ ]),
302
+ ];
303
+ }
304
+ }
305
+ function buildDocSections(outline, title) {
306
+ switch (outline) {
307
+ case 'report':
308
+ return [
309
+ section('section-title', 'title', title, [
310
+ { type: 'heading', text: title, level: 1 },
311
+ { type: 'text', markdown: placeholder('report subtitle or period') },
312
+ ]),
313
+ section('section-summary', 'summary', 'Executive summary', [
314
+ { type: 'heading', text: 'Executive summary', level: 2 },
315
+ { type: 'text', markdown: 'One-paragraph summary placeholder.' },
316
+ ]),
317
+ section('section-status', 'status', 'Status', [
318
+ { type: 'heading', text: 'Status', level: 2 },
319
+ {
320
+ type: 'stat',
321
+ items: [
322
+ { value: '—', label: 'Metric one' },
323
+ { value: '—', label: 'Metric two' },
324
+ { value: '—', label: 'Metric three' },
325
+ ],
326
+ },
327
+ { type: 'text', markdown: placeholder('what changed this period') },
328
+ ]),
329
+ section('section-findings', 'findings', 'Findings', [
330
+ { type: 'heading', text: 'Findings', level: 2 },
331
+ {
332
+ type: 'list',
333
+ style: 'number',
334
+ items: ['Finding one — replace with evidence', 'Finding two — replace with evidence'],
335
+ },
336
+ ]),
337
+ section('section-next', 'next-steps', 'Next steps', [
338
+ { type: 'heading', text: 'Next steps', level: 2 },
339
+ {
340
+ type: 'list',
341
+ style: 'check',
342
+ items: ['Action one', 'Action two', 'Action three'],
343
+ },
344
+ ]),
345
+ ];
346
+ case 'handbook':
347
+ return [
348
+ section('section-title', 'title', title, [{ type: 'heading', text: title, level: 1 }]),
349
+ section('section-summary', 'summary', 'How to use this guide', [
350
+ { type: 'heading', text: 'How to use this guide', level: 2 },
351
+ { type: 'text', markdown: placeholder('audience and scope') },
352
+ ]),
353
+ section('section-toc', 'toc', 'Contents', [
354
+ { type: 'heading', text: 'Contents', level: 2 },
355
+ {
356
+ type: 'list',
357
+ style: 'number',
358
+ items: ['Chapter one', 'Chapter two', 'Chapter three'],
359
+ },
360
+ ]),
361
+ section('section-body', 'body', 'Chapters', [
362
+ { type: 'heading', text: 'Chapters', level: 2 },
363
+ { type: 'text', markdown: placeholder('the first chapter body') },
364
+ { type: 'divider', label: 'Next chapter' },
365
+ { type: 'text', markdown: placeholder('the next chapter body') },
366
+ ]),
367
+ section('section-appendix', 'appendix', 'Appendix', [
368
+ { type: 'heading', text: 'Appendix', level: 2 },
369
+ { type: 'text', markdown: placeholder('glossary or links') },
370
+ ]),
371
+ ];
372
+ case 'one-pager':
373
+ return [
374
+ section('section-title', 'title', title, [{ type: 'heading', text: title, level: 1 }]),
375
+ section('section-summary', 'summary', 'At a glance', [
376
+ { type: 'heading', text: 'At a glance', level: 2 },
377
+ { type: 'text', markdown: 'One-paragraph summary placeholder.' },
378
+ ]),
379
+ section('section-body', 'body', 'Details', [
380
+ { type: 'heading', text: 'Details', level: 2 },
381
+ {
382
+ type: 'list',
383
+ style: 'bullet',
384
+ items: ['Point one', 'Point two', 'Point three'],
385
+ },
386
+ ]),
387
+ section('section-cta', 'cta', 'Next step', [
388
+ { type: 'cta', label: 'Next step', href: '#next' },
389
+ ]),
390
+ ];
391
+ case 'proposal':
392
+ return [
393
+ section('section-title', 'title', title, [
394
+ { type: 'heading', text: title, level: 1 },
395
+ { type: 'text', markdown: placeholder('client and date') },
396
+ ]),
397
+ section('section-summary', 'summary', 'Overview', [
398
+ { type: 'heading', text: 'Overview', level: 2 },
399
+ { type: 'text', markdown: placeholder('problem and proposed approach') },
400
+ ]),
401
+ section('section-scope', 'scope', 'Scope', [
402
+ { type: 'heading', text: 'Scope', level: 2 },
403
+ {
404
+ type: 'list',
405
+ style: 'bullet',
406
+ items: ['In scope item', 'In scope item', 'Out of scope item'],
407
+ },
408
+ ]),
409
+ section('section-timeline', 'timeline', 'Timeline', [
410
+ { type: 'heading', text: 'Timeline', level: 2 },
411
+ {
412
+ type: 'list',
413
+ style: 'number',
414
+ items: ['Phase one', 'Phase two', 'Phase three'],
415
+ },
416
+ ]),
417
+ section('section-next', 'next-steps', 'Decision', [
418
+ { type: 'heading', text: 'Decision', level: 2 },
419
+ { type: 'text', markdown: placeholder('the ask and how to accept') },
420
+ ]),
421
+ ];
422
+ default:
423
+ return [
424
+ section('section-title', 'title', title, [
425
+ { type: 'heading', text: title, level: 1 },
426
+ { type: 'text', markdown: 'Document title block.' },
427
+ ]),
428
+ section('section-summary', 'summary', 'Summary', [
429
+ { type: 'heading', text: 'Summary', level: 2 },
430
+ { type: 'text', markdown: 'One-paragraph summary placeholder.' },
431
+ ]),
432
+ section('section-body', 'body', 'Body', [
433
+ { type: 'heading', text: 'Body', level: 2 },
434
+ { type: 'text', markdown: 'Add the main document sections here.' },
435
+ ]),
436
+ ];
437
+ }
438
+ }
439
+ function outlineRationale(kind, outline, omissions) {
440
+ const lines = [
441
+ `${titleCase(kind)} outline: ${outline}.`,
442
+ 'Sections use the bounded block vocabulary (heading, text, list, cta, stat, quote, callout, divider).',
443
+ 'Draft scaffolding only — no fabricated facts, no app entities or roles.',
444
+ ];
445
+ for (const omission of omissions)
446
+ lines.push(`Omission: ${omission}`);
447
+ if (omissions.length === 0) {
448
+ lines.push('No interactive backend features were requested beyond static content.');
449
+ }
450
+ return lines;
451
+ }
452
+ /** Build a deterministic page outline from a brief. */
453
+ export function buildPageOutline(brief, options = {}) {
454
+ const source = brief.slice(0, 5_000);
455
+ const outline = selectPageOutline(source);
456
+ const title = pickTitle(source, 'page', options.title);
457
+ const omissions = detectPageDocOmissions(source);
458
+ const sections = buildPageSections(outline, title);
459
+ return {
460
+ outline,
461
+ title,
462
+ sections,
463
+ omissions,
464
+ rationale: outlineRationale('page', outline, omissions),
465
+ };
466
+ }
467
+ /** Build a deterministic doc outline from a brief. */
468
+ export function buildDocOutline(brief, options = {}) {
469
+ const source = brief.slice(0, 5_000);
470
+ const outline = selectDocOutline(source);
471
+ const title = pickTitle(source, 'doc', options.title);
472
+ const omissions = detectPageDocOmissions(source);
473
+ const sections = buildDocSections(outline, title);
474
+ return {
475
+ outline,
476
+ title,
477
+ sections,
478
+ omissions,
479
+ rationale: outlineRationale('doc', outline, omissions),
480
+ };
481
+ }
482
+ /** Stable ordered section ids for assertions and lint. */
483
+ export function sectionIds(sections) {
484
+ return sections.map((s) => s.id);
485
+ }
@@ -0,0 +1,59 @@
1
+ import type { BlueprintKind } from '../kinds/kind.ts';
2
+ import { type Blueprint, type BlueprintInput } from '../schema.ts';
3
+ import { type ContentBriefResult } from './content-synthesize.ts';
4
+ import { type BriefKindDetection } from './kind-detection.ts';
5
+ /**
6
+ * Brief → blueprint, offline and deterministic: the same brief (and options)
7
+ * always gives the same blueprint, with no network, model or randomness.
8
+ *
9
+ * Kind dispatch (ADR-036): `detectBriefKind` runs first. An explicit `kind`
10
+ * overrides cues. Detected `deck` / `page` / `doc` briefs go to kind-specific
11
+ * synthesizers before any app archetype is scored. App briefs keep the same
12
+ * result fields and deterministic JSON as before kinds existed.
13
+ */
14
+ export interface BriefOptions {
15
+ /** Force an archetype id (see `ARCHETYPE_IDS`) instead of keyword scoring. App only. */
16
+ archetype?: string;
17
+ /** Product name; default: a fictional name derived from the brief. */
18
+ name?: string;
19
+ /**
20
+ * Explicit blueprint kind (`app` | `deck` | `page` | `doc`). Overrides every
21
+ * cue. An unknown string throws a stable `RangeError`.
22
+ */
23
+ kind?: BlueprintKind | string;
24
+ }
25
+ export type SuggestedLayout = 'sidebar' | 'mobile-tabs';
26
+ /** App brief result (the historical shape, plus kind metadata). */
27
+ export interface AppBriefResult {
28
+ kind: 'app';
29
+ kindDetection: BriefKindDetection;
30
+ /** Parsed and validated app blueprint. */
31
+ blueprint: Blueprint;
32
+ /** The same blueprint as plain JSON, ready to write as `blueprint.json`. */
33
+ json: BlueprintInput;
34
+ archetype: string;
35
+ /** App layout the brief suggests (phone briefs → bottom tabs). */
36
+ layout: SuggestedLayout;
37
+ /** One line per inference, in order. */
38
+ rationale: string[];
39
+ }
40
+ export type BriefResult = AppBriefResult | ContentBriefResult;
41
+ export declare function isAppBriefResult(result: BriefResult): result is AppBriefResult;
42
+ export declare function isContentBriefResult(result: BriefResult): result is ContentBriefResult;
43
+ /** Longer briefs are cut here; the synthesizer reads keywords, not essays. */
44
+ export declare const MAX_BRIEF_LENGTH = 5000;
45
+ /**
46
+ * App-only synthesis (archetype scoring, entities, screens). Extracted so
47
+ * `blueprintFromBrief` can dispatch content kinds without scoring apps.
48
+ * Exported for tests that need the app path in isolation.
49
+ */
50
+ export declare function synthesizeAppFromBrief(brief: string, options?: BriefOptions, kindDetection?: BriefKindDetection): AppBriefResult;
51
+ /**
52
+ * A blueprint (app or content draft) from a short product brief, plus the
53
+ * reasons for each choice. Same brief and options → same result.
54
+ *
55
+ * 1. Resolve kind (`options.kind` or `detectBriefKind`).
56
+ * 2. Content kinds → kind-specific synthesizers (no app archetype scoring).
57
+ * 3. App → existing archetype / entity / screen path (byte-compatible JSON).
58
+ */
59
+ export declare function blueprintFromBrief(brief: string, options?: BriefOptions): BriefResult;