@owlmeans/viable-common 0.0.33 → 0.0.35
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.
- package/README.md +3 -3
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/agent-presentation/SKILL.md +1 -1
- package/agent-meta/skills/viable-common/SKILL.md +93 -62
- package/build/ba/index.d.ts +0 -1
- package/build/ba/index.d.ts.map +1 -1
- package/build/ba/index.js +0 -1
- package/build/ba/index.js.map +1 -1
- package/build/blueprint/consts.d.ts +38 -0
- package/build/blueprint/consts.d.ts.map +1 -1
- package/build/blueprint/consts.js +40 -0
- package/build/blueprint/consts.js.map +1 -1
- package/build/blueprint/index.d.ts +0 -3
- package/build/blueprint/index.d.ts.map +1 -1
- package/build/blueprint/index.js +0 -3
- package/build/blueprint/index.js.map +1 -1
- package/build/blueprint/types.d.ts +5 -5
- package/build/intent/consts.d.ts +57 -0
- package/build/intent/consts.d.ts.map +1 -0
- package/build/intent/consts.js +58 -0
- package/build/intent/consts.js.map +1 -0
- package/build/intent/entrypoints.d.ts +27 -0
- package/build/intent/entrypoints.d.ts.map +1 -0
- package/build/intent/entrypoints.js +28 -0
- package/build/intent/entrypoints.js.map +1 -0
- package/build/intent/errors.d.ts +20 -0
- package/build/intent/errors.d.ts.map +1 -0
- package/build/intent/errors.js +28 -0
- package/build/intent/errors.js.map +1 -0
- package/build/intent/flow.d.ts +17 -0
- package/build/intent/flow.d.ts.map +1 -0
- package/build/intent/flow.js +65 -0
- package/build/intent/flow.js.map +1 -0
- package/build/intent/index.d.ts +7 -0
- package/build/intent/index.d.ts.map +1 -0
- package/build/intent/index.js +6 -0
- package/build/intent/index.js.map +1 -0
- package/build/intent/schemas.d.ts +10 -0
- package/build/intent/schemas.d.ts.map +1 -0
- package/build/intent/schemas.js +24 -0
- package/build/intent/schemas.js.map +1 -0
- package/build/intent/types.d.ts +40 -0
- package/build/intent/types.d.ts.map +1 -0
- package/build/intent/types.js +2 -0
- package/build/intent/types.js.map +1 -0
- package/build/scaffold/consts.d.ts +14 -1
- package/build/scaffold/consts.d.ts.map +1 -1
- package/build/scaffold/consts.js +10 -0
- package/build/scaffold/consts.js.map +1 -1
- package/build/scaffold/schemas.d.ts +7 -0
- package/build/scaffold/schemas.d.ts.map +1 -1
- package/build/scaffold/schemas.js +119 -68
- package/build/scaffold/schemas.js.map +1 -1
- package/build/scaffold/types.d.ts +62 -24
- package/build/scaffold/types.d.ts.map +1 -1
- package/build/skills/consts.d.ts +0 -34
- package/build/skills/consts.d.ts.map +1 -1
- package/build/skills/consts.js +0 -106
- package/build/skills/consts.js.map +1 -1
- package/build/skills/index.d.ts +0 -1
- package/build/skills/index.d.ts.map +1 -1
- package/build/skills/index.js +0 -1
- package/build/skills/index.js.map +1 -1
- package/build/skills/roles.d.ts +0 -9
- package/build/skills/roles.d.ts.map +1 -1
- package/build/skills/roles.js +0 -226
- package/build/skills/roles.js.map +1 -1
- package/build/ux/schemas.js +2 -2
- package/build/ux/schemas.js.map +1 -1
- package/package.json +17 -7
- package/src/ba/index.ts +0 -1
- package/src/blueprint/consts.ts +40 -0
- package/src/blueprint/index.ts +0 -3
- package/src/blueprint/types.ts +5 -5
- package/src/intent/consts.ts +66 -0
- package/src/intent/entrypoints.ts +43 -0
- package/src/intent/errors.ts +33 -0
- package/src/intent/flow.ts +67 -0
- package/src/intent/index.ts +6 -0
- package/src/intent/schemas.ts +27 -0
- package/src/intent/types.ts +45 -0
- package/src/scaffold/consts.ts +14 -1
- package/src/scaffold/schemas.ts +125 -69
- package/src/scaffold/types.ts +64 -23
- package/src/skills/consts.ts +0 -112
- package/src/skills/index.ts +0 -1
- package/src/skills/roles.ts +0 -267
- package/src/ux/schemas.ts +2 -2
- package/tests/blueprint.spec.ts +2 -66
- package/tests/intent.spec.ts +121 -0
- package/tests/scaffold.spec.ts +73 -13
- package/src/ba/schemas.ts +0 -196
- package/src/blueprint/case.ts +0 -320
- package/src/blueprint/resolve.ts +0 -136
- package/src/blueprint/schemas.ts +0 -44
- package/src/skills/catalogue.ts +0 -1864
- package/tests/skills.spec.ts +0 -125
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { JSONSchemaType } from 'ajv'
|
|
2
|
+
import { INTENT_PROMPT_MAX, INTENT_REF_LENGTH, INTENT_REF_PATTERN } from './consts.js'
|
|
3
|
+
import type { IntentPickupBody, IntentStashBody } from './types.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The stash body. `consent` is a constant `true`, not a boolean: the data-processing confirmation is
|
|
7
|
+
* a precondition of the request existing, so the wire refuses its absence instead of the handler
|
|
8
|
+
* having to remember to look.
|
|
9
|
+
*/
|
|
10
|
+
export const IntentStashBodySchema: JSONSchemaType<IntentStashBody> = {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
prompt: { type: 'string', minLength: 1, maxLength: INTENT_PROMPT_MAX },
|
|
14
|
+
consent: { type: 'boolean', const: true },
|
|
15
|
+
},
|
|
16
|
+
required: ['prompt', 'consent'],
|
|
17
|
+
additionalProperties: false,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const IntentPickupBodySchema: JSONSchemaType<IntentPickupBody> = {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
ref: { type: 'string', minLength: INTENT_REF_LENGTH, maxLength: INTENT_REF_LENGTH, pattern: INTENT_REF_PATTERN },
|
|
24
|
+
},
|
|
25
|
+
required: ['ref'],
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ResourceRecord } from '@owlmeans/resource'
|
|
2
|
+
|
|
3
|
+
/** What the public site sends: the prompt and the person's confirmation of its processing. */
|
|
4
|
+
export interface IntentStashBody {
|
|
5
|
+
prompt: string
|
|
6
|
+
/** Always `true` — the site's checkbox; a request without it is refused by the schema. */
|
|
7
|
+
consent: true
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** The reference the platform answers with, and the moment its record expires. */
|
|
11
|
+
export interface IntentStashResult {
|
|
12
|
+
ref: string
|
|
13
|
+
/** Epoch milliseconds. */
|
|
14
|
+
expiresAt: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IntentPickupBody {
|
|
18
|
+
ref: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IntentPickupResult {
|
|
22
|
+
prompt: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The prompt as it waits in the visitor's browser between pickup and the decision on the home
|
|
27
|
+
* screen. `expiresAt` is checked on read — browser storage has no TTL of its own.
|
|
28
|
+
*/
|
|
29
|
+
export interface IntentDraft extends ResourceRecord {
|
|
30
|
+
id: string
|
|
31
|
+
/** The reference it was picked up with; a reload of the landing reuses the draft of that ref. */
|
|
32
|
+
ref: string
|
|
33
|
+
prompt: string
|
|
34
|
+
expiresAt: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Options of {@link makeIntentProtocols}.
|
|
39
|
+
*/
|
|
40
|
+
export interface IntentProtocolOptions {
|
|
41
|
+
/** Path prefix of the guest API; defaults to `/public/intent`. */
|
|
42
|
+
path?: string
|
|
43
|
+
/** Path of the landing screen; defaults to `/start`. */
|
|
44
|
+
landing?: string
|
|
45
|
+
}
|
package/src/scaffold/consts.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BentoFragmentKind } from './types.js'
|
|
1
|
+
import type { BentoFragmentKind, LandingGateFieldKind } from './types.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The shapes a drawn widget can take.
|
|
@@ -34,3 +34,16 @@ export const AREA_SECTION_CAP = 6
|
|
|
34
34
|
* value nothing can draw is a tile with an empty bottom.
|
|
35
35
|
*/
|
|
36
36
|
export const BENTO_FRAGMENT_KINDS: BentoFragmentKind[] = ['list', 'note', 'people', 'steps']
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The controls a landing-gate field can be — the simplest native inputs, and no picker of any
|
|
40
|
+
* kind. A closed set for the same reason as {@link BENTO_FRAGMENT_KINDS}: each value is one plain
|
|
41
|
+
* element the seed's gate knows how to draw.
|
|
42
|
+
*/
|
|
43
|
+
export const LANDING_GATE_FIELD_KINDS: LandingGateFieldKind[] = ['text', 'select', 'number', 'date']
|
|
44
|
+
|
|
45
|
+
/** The most fields a landing gate carries. A form longer than this is a screen, not an entry. */
|
|
46
|
+
export const LANDING_GATE_MAX_FIELDS = 3
|
|
47
|
+
|
|
48
|
+
/** The options a `select` field carries: fewer is no choice, more is a list to browse. */
|
|
49
|
+
export const LANDING_GATE_OPTIONS = { min: 2, max: 6 } as const
|
package/src/scaffold/schemas.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { JSONSchemaType } from 'ajv'
|
|
2
2
|
|
|
3
3
|
import { ProjectArea } from '../areas/consts.js'
|
|
4
|
-
import { BENTO_FRAGMENT_KINDS, WidgetKind } from './consts.js'
|
|
4
|
+
import { BENTO_FRAGMENT_KINDS, LANDING_GATE_FIELD_KINDS, WidgetKind } from './consts.js'
|
|
5
5
|
import type {
|
|
6
|
-
BentoFragment, GuestHomePlan, LandingGatePlan, ProductIdentity,
|
|
6
|
+
BentoFragment, GuestHomePlan, LandingGateAnswer, LandingGateField, LandingGatePlan, ProductIdentity,
|
|
7
7
|
ScaffoldAreaPlan, ScaffoldPlan, ScaffoldStoryPlan, SketchContent,
|
|
8
8
|
} from './types.js'
|
|
9
9
|
|
|
@@ -83,7 +83,8 @@ const ScaffoldStoryPlanSchema: JSONSchemaType<ScaffoldStoryPlan> = {
|
|
|
83
83
|
screen: {
|
|
84
84
|
type: 'string',
|
|
85
85
|
description: 'The main screen this story will need, named "{section}/{purpose}" -'
|
|
86
|
-
+ ' e.g. "Requests/Submit
|
|
86
|
+
+ ' e.g. "Requests/Submit request". The purpose is one or two words, never a sentence.'
|
|
87
|
+
+ ' The section part must equal the section above'
|
|
87
88
|
},
|
|
88
89
|
widget: {
|
|
89
90
|
type: 'object',
|
|
@@ -225,99 +226,129 @@ const BentoFragmentSchema: JSONSchemaType<BentoFragment> = {
|
|
|
225
226
|
additionalProperties: false,
|
|
226
227
|
}
|
|
227
228
|
|
|
228
|
-
const
|
|
229
|
+
const LandingGateFieldSchema: JSONSchemaType<LandingGateField> = {
|
|
229
230
|
type: 'object',
|
|
230
|
-
title: '
|
|
231
|
-
description: '
|
|
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',
|
|
231
|
+
title: 'LandingGateField',
|
|
232
|
+
description: 'One simple, non-sensitive field of the landing gate',
|
|
234
233
|
properties: {
|
|
235
|
-
|
|
234
|
+
name: {
|
|
236
235
|
type: 'string',
|
|
237
|
-
description: 'The
|
|
236
|
+
description: 'The camelCase key its value travels under, one word - e.g. "dish"'
|
|
238
237
|
},
|
|
239
|
-
|
|
240
|
-
type: 'string', nullable: true,
|
|
241
|
-
description: 'Leave it out - the platform fills it'
|
|
242
|
-
},
|
|
243
|
-
question: {
|
|
238
|
+
label: {
|
|
244
239
|
type: 'string',
|
|
245
|
-
description: 'The
|
|
246
|
-
+ ' in your pantry?"'
|
|
240
|
+
description: 'The visible label in the end user\'s words, 2-5 words - e.g. "What are you baking?"'
|
|
247
241
|
},
|
|
248
|
-
|
|
249
|
-
label: {
|
|
242
|
+
kind: {
|
|
250
243
|
type: 'string',
|
|
251
|
-
|
|
244
|
+
enum: LANDING_GATE_FIELD_KINDS,
|
|
245
|
+
description: 'text (a short line, the default), select (ONE choice out of 2-6 plain options),'
|
|
246
|
+
+ ' number, or date. Nothing else exists: no chips, no toggles, no multiple choice'
|
|
252
247
|
},
|
|
253
|
-
|
|
254
|
-
type: '
|
|
255
|
-
|
|
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'
|
|
248
|
+
placeholder: {
|
|
249
|
+
type: 'string', nullable: true,
|
|
250
|
+
description: 'text or number only: an example value, e.g. "Rye sourdough". Never personal'
|
|
258
251
|
},
|
|
259
|
-
|
|
260
|
-
type: 'array',
|
|
252
|
+
options: {
|
|
253
|
+
type: 'array', nullable: true,
|
|
261
254
|
items: { type: 'string' },
|
|
262
|
-
description: '
|
|
263
|
-
+ ' already shows results'
|
|
255
|
+
description: 'select only: 2 to 6 plain options, 1-3 words each'
|
|
264
256
|
},
|
|
257
|
+
},
|
|
258
|
+
required: ['name', 'label', 'kind'],
|
|
259
|
+
additionalProperties: false,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** What the gate's texts tell the planning model — one place, shared by the answer and stored schemas. */
|
|
263
|
+
const GATE_TEXT = {
|
|
264
|
+
description: 'The working entry into the key end-user workflow, drawn in the hero instead of the'
|
|
265
|
+
+ ' call-to-action buttons: ONE SMALL FORM - a heading, one to three simple non-sensitive'
|
|
266
|
+
+ ' fields, one button. A guest fills it in, signs in, and continues with what they entered.'
|
|
267
|
+
+ ' No choices to browse, no sample results, no counts, no previews. Every text is in the end'
|
|
268
|
+
+ ' user\'s words',
|
|
269
|
+
story: 'The code of the landing story, copied EXACTLY as given to you. Never shown',
|
|
270
|
+
target: 'Leave it out - the platform fills it',
|
|
271
|
+
question: 'The heading of the form, 3-6 words, in the end user\'s words - e.g. "What are you'
|
|
272
|
+
+ ' baking?"',
|
|
273
|
+
fields: 'The fields, AS FEW AS POSSIBLE: one whenever one is enough, never more than three. They'
|
|
274
|
+
+ ' are the first things the story\'s own screen asks for, in the same words',
|
|
275
|
+
cta: 'The button label, a verb first, 2-3 words and an arrow - e.g. "Start my post →"',
|
|
276
|
+
note: 'One short muted line under the form: no account is needed yet and what was entered comes'
|
|
277
|
+
+ ' along - e.g. "No account needed. What you enter comes with you."',
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** The pre-form keys of a stored gate: optional, described as history, never offered to the model. */
|
|
281
|
+
const LEGACY_GATE_NOTE = 'Pre-form shape, never written'
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* What the planning MODEL answers for the gate: the form and nothing older. The stored schema below
|
|
285
|
+
* adds the pre-form keys as optional, so a plan written before the gate became a form still
|
|
286
|
+
* validates; the model is never offered them.
|
|
287
|
+
*/
|
|
288
|
+
const LandingGateAnswerSchema: JSONSchemaType<LandingGateAnswer> = {
|
|
289
|
+
type: 'object',
|
|
290
|
+
title: 'LandingGatePlan',
|
|
291
|
+
description: GATE_TEXT.description,
|
|
292
|
+
properties: {
|
|
293
|
+
story: { type: 'string', description: GATE_TEXT.story },
|
|
294
|
+
target: { type: 'string', nullable: true, description: GATE_TEXT.target },
|
|
295
|
+
question: { type: 'string', description: GATE_TEXT.question },
|
|
296
|
+
fields: { type: 'array', items: LandingGateFieldSchema, description: GATE_TEXT.fields },
|
|
297
|
+
cta: { type: 'string', description: GATE_TEXT.cta },
|
|
298
|
+
note: { type: 'string', description: GATE_TEXT.note },
|
|
299
|
+
},
|
|
300
|
+
required: ['story', 'question', 'fields', 'cta', 'note'],
|
|
301
|
+
additionalProperties: false,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* The gate as a plan may STORE it: the form, plus the pre-form keys (chips, sample records, a live
|
|
306
|
+
* count) as optional. A schema that validates a stored document only ever grows optional.
|
|
307
|
+
*/
|
|
308
|
+
const LandingGatePlanSchema: JSONSchemaType<LandingGatePlan> = {
|
|
309
|
+
type: 'object',
|
|
310
|
+
title: 'LandingGatePlan',
|
|
311
|
+
description: GATE_TEXT.description,
|
|
312
|
+
properties: {
|
|
313
|
+
story: { type: 'string', description: GATE_TEXT.story },
|
|
314
|
+
target: { type: 'string', nullable: true, description: GATE_TEXT.target },
|
|
315
|
+
question: { type: 'string', description: GATE_TEXT.question },
|
|
316
|
+
fields: { type: 'array', nullable: true, items: LandingGateFieldSchema, description: GATE_TEXT.fields },
|
|
317
|
+
cta: { type: 'string', description: GATE_TEXT.cta },
|
|
318
|
+
note: { type: 'string', description: GATE_TEXT.note },
|
|
319
|
+
hint: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
320
|
+
label: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
321
|
+
inputs: { type: 'array', nullable: true, items: { type: 'string' }, description: LEGACY_GATE_NOTE },
|
|
322
|
+
selected: { type: 'array', nullable: true, items: { type: 'string' }, description: LEGACY_GATE_NOTE },
|
|
265
323
|
results: {
|
|
266
|
-
type: 'array',
|
|
324
|
+
type: 'array', nullable: true,
|
|
267
325
|
items: {
|
|
268
326
|
type: 'object',
|
|
269
327
|
properties: {
|
|
270
|
-
title: { type: 'string'
|
|
271
|
-
meta: {
|
|
272
|
-
|
|
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
|
-
},
|
|
328
|
+
title: { type: 'string' },
|
|
329
|
+
meta: { type: 'string' },
|
|
330
|
+
needs: { type: 'array', items: { type: 'string' } },
|
|
280
331
|
},
|
|
281
332
|
required: ['title', 'meta', 'needs'],
|
|
282
333
|
additionalProperties: false,
|
|
283
334
|
},
|
|
284
|
-
description:
|
|
335
|
+
description: LEGACY_GATE_NOTE
|
|
285
336
|
},
|
|
286
337
|
count: {
|
|
287
|
-
type: 'object',
|
|
338
|
+
type: 'object', nullable: true,
|
|
288
339
|
properties: {
|
|
289
|
-
one: { type: 'string'
|
|
290
|
-
many: { type: 'string'
|
|
291
|
-
none: { type: 'string'
|
|
340
|
+
one: { type: 'string' },
|
|
341
|
+
many: { type: 'string' },
|
|
342
|
+
none: { type: 'string' },
|
|
292
343
|
},
|
|
293
344
|
required: ['one', 'many', 'none'],
|
|
294
345
|
additionalProperties: false,
|
|
295
|
-
description:
|
|
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"'
|
|
346
|
+
description: LEGACY_GATE_NOTE
|
|
315
347
|
},
|
|
348
|
+
empty: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
349
|
+
lock: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
316
350
|
},
|
|
317
|
-
required: [
|
|
318
|
-
'story', 'question', 'hint', 'label', 'inputs', 'selected', 'results', 'count', 'empty',
|
|
319
|
-
'note', 'cta', 'lock',
|
|
320
|
-
],
|
|
351
|
+
required: ['story', 'question', 'cta', 'note'],
|
|
321
352
|
additionalProperties: false,
|
|
322
353
|
}
|
|
323
354
|
|
|
@@ -535,3 +566,28 @@ export const ScaffoldPlanSchema: JSONSchemaType<ScaffoldPlan> = {
|
|
|
535
566
|
required: ['identity', 'guestHome', 'areas', 'stories', 'motifs'],
|
|
536
567
|
additionalProperties: false,
|
|
537
568
|
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* The plan as the planning MODEL answers it — {@link ScaffoldPlanSchema} with the gate held to the
|
|
572
|
+
* form. The stored schema keeps the pre-form gate keys optional so an old plan still validates;
|
|
573
|
+
* offering them to the model would let it write a chip picker again. Typed as the stored plan
|
|
574
|
+
* because every answer is one: the answer's gate is a subset of the stored gate.
|
|
575
|
+
*/
|
|
576
|
+
export const ScaffoldPlanAnswerSchema = {
|
|
577
|
+
...ScaffoldPlanSchema,
|
|
578
|
+
properties: {
|
|
579
|
+
...ScaffoldPlanSchema.properties,
|
|
580
|
+
guestHome: {
|
|
581
|
+
...GuestHomePlanSchema,
|
|
582
|
+
properties: {
|
|
583
|
+
...GuestHomePlanSchema.properties,
|
|
584
|
+
gate: {
|
|
585
|
+
...LandingGateAnswerSchema,
|
|
586
|
+
nullable: true,
|
|
587
|
+
description: 'ONLY when you were given a landing story; omit it otherwise. '
|
|
588
|
+
+ LandingGateAnswerSchema.description,
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
} as unknown as JSONSchemaType<ScaffoldPlan>
|
package/src/scaffold/types.ts
CHANGED
|
@@ -119,42 +119,83 @@ export interface BentoFragment {
|
|
|
119
119
|
rows: { label: string, value?: string }[]
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* What kind of control a landing-gate field is. A CLOSED set of the simplest native inputs: each
|
|
124
|
+
* value is one plain element, so there is nothing here a visitor has to learn, and nothing that
|
|
125
|
+
* can grow into a picker, a chip group or a preview.
|
|
126
|
+
*/
|
|
127
|
+
export type LandingGateFieldKind = 'text' | 'select' | 'number' | 'date'
|
|
128
|
+
|
|
129
|
+
/** One simple, non-sensitive field of the landing gate. */
|
|
130
|
+
export interface LandingGateField {
|
|
131
|
+
/** The camelCase key the entered value travels under in the landing handoff: "dish". */
|
|
132
|
+
name: string
|
|
133
|
+
/** The visible label, in the end user's words: "What are you baking?". */
|
|
134
|
+
label: string
|
|
135
|
+
kind: LandingGateFieldKind
|
|
136
|
+
/** An example value shown inside an empty text or number field — never a personal one. */
|
|
137
|
+
placeholder?: string
|
|
138
|
+
/** `select` only: 2–6 plain options. */
|
|
139
|
+
options?: string[]
|
|
140
|
+
}
|
|
141
|
+
|
|
122
142
|
/**
|
|
123
143
|
* The landing gate — the working entry into the product's key END-USER workflow, drawn in the
|
|
124
144
|
* hero where the call-to-action pills would otherwise be.
|
|
125
145
|
*
|
|
126
|
-
* A guest
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* an address the model invented — `target` is filled by
|
|
146
|
+
* A guest fills in one to three simple non-sensitive fields on the landing page and signs in to
|
|
147
|
+
* continue on the story's full-scale screen with what they entered already in place. It is a small
|
|
148
|
+
* form and nothing more: no choices to browse, no sample results, no counts. Every text is copy in
|
|
149
|
+
* the end user's own words; nothing here is an address the model invented — `target` is filled by
|
|
150
|
+
* code.
|
|
151
|
+
*
|
|
152
|
+
* The keys after `note` are the shape a plan had before the gate became a form (chips, sample
|
|
153
|
+
* records, a live count). They stay so a plan STORED under the old shape still validates and
|
|
154
|
+
* still reads; nothing writes them and nothing draws from them, and a gate that carries no
|
|
155
|
+
* `fields` is drawn as no gate at all.
|
|
130
156
|
*/
|
|
131
157
|
export interface LandingGatePlan {
|
|
132
158
|
/** The key end-user story's code — never rendered. */
|
|
133
159
|
story: string
|
|
134
160
|
/** Entrypoint ALIAS of the story's full-scale screen in the user area (filled by code, not the model). */
|
|
135
161
|
target?: string
|
|
136
|
-
/** The
|
|
162
|
+
/** The heading of the form, in the end user's words: "What are you baking?". */
|
|
137
163
|
question: string
|
|
138
|
-
/** The
|
|
139
|
-
|
|
140
|
-
/** The
|
|
141
|
-
|
|
142
|
-
/**
|
|
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.". */
|
|
164
|
+
/** The fields, one to three — one whenever one is enough. Absent only on a plan stored under the old shape. */
|
|
165
|
+
fields?: LandingGateField[]
|
|
166
|
+
/** The one button's label: "Start my post →". */
|
|
167
|
+
cta: string
|
|
168
|
+
/** The muted line under the form: "No account needed. What you enter comes with you.". */
|
|
153
169
|
note: string
|
|
154
|
-
/**
|
|
170
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
171
|
+
hint?: string
|
|
172
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
173
|
+
label?: string
|
|
174
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
175
|
+
inputs?: string[]
|
|
176
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
177
|
+
selected?: string[]
|
|
178
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
179
|
+
results?: { title: string, meta: string, needs: string[] }[]
|
|
180
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
181
|
+
count?: { one: string, many: string, none: string }
|
|
182
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
183
|
+
empty?: string
|
|
184
|
+
/** @deprecated Pre-form shape, kept for stored plans only. */
|
|
185
|
+
lock?: string
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The gate as the planning MODEL answers it: the form, and none of the pre-form keys. Assignable
|
|
190
|
+
* to {@link LandingGatePlan}, which is what a plan stores.
|
|
191
|
+
*/
|
|
192
|
+
export interface LandingGateAnswer {
|
|
193
|
+
story: string
|
|
194
|
+
target?: string
|
|
195
|
+
question: string
|
|
196
|
+
fields: LandingGateField[]
|
|
155
197
|
cta: string
|
|
156
|
-
|
|
157
|
-
lock: string
|
|
198
|
+
note: string
|
|
158
199
|
}
|
|
159
200
|
|
|
160
201
|
/** The guest area's landing page — the product's public face. */
|
package/src/skills/consts.ts
CHANGED
|
@@ -85,115 +85,3 @@ export enum ViableSkill {
|
|
|
85
85
|
LibrariesAi = 'libraries-ai',
|
|
86
86
|
LibrariesGame = 'libraries-game',
|
|
87
87
|
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Sort weights. Skills render in this order inside the cached block, so the sequence is
|
|
91
|
-
* part of the cache key — reordering them invalidates every prefix at once. Grouped in
|
|
92
|
-
* tens to leave room for a consumer's own skills between them.
|
|
93
|
-
*/
|
|
94
|
-
export const SKILL_ORDER: Record<ViableSkill, number> = {
|
|
95
|
-
[ViableSkill.OutputSourceOnly]: 10,
|
|
96
|
-
[ViableSkill.OutputTextOnly]: 10,
|
|
97
|
-
[ViableSkill.ScopeDiscipline]: 15,
|
|
98
|
-
[ViableSkill.MainFlowFocus]: 12,
|
|
99
|
-
[ViableSkill.NonTypescriptOutput]: 18,
|
|
100
|
-
[ViableSkill.TsStyle]: 20,
|
|
101
|
-
[ViableSkill.TsImports]: 30,
|
|
102
|
-
[ViableSkill.ReactComponents]: 40,
|
|
103
|
-
[ViableSkill.OwlMeansEntrypoints]: 41,
|
|
104
|
-
[ViableSkill.OwlMeansNav]: 42,
|
|
105
|
-
[ViableSkill.PermissionModel]: 43,
|
|
106
|
-
[ViableSkill.ShadcnUi]: 44,
|
|
107
|
-
[ViableSkill.OwlmeansState]: 50,
|
|
108
|
-
[ViableSkill.FormFeedback]: 51,
|
|
109
|
-
[ViableSkill.StoreAccess]: 52,
|
|
110
|
-
[ViableSkill.ViewModelNaming]: 55,
|
|
111
|
-
// After the frontend rules it builds on (entrypoints, nav, shadcn, form feedback, the store), and
|
|
112
|
-
// before the backend block — it is only ever carried by analysis and frontend personas.
|
|
113
|
-
[ViableSkill.LandingGate]: 56,
|
|
114
|
-
// Analysis and frontend read it the same way LandingGate is read — after the frontend rules it
|
|
115
|
-
// references (nav, shadcn) — and a backend coder reads it as a server-side check, so it sits
|
|
116
|
-
// just ahead of the backend block for either audience.
|
|
117
|
-
[ViableSkill.MarketingConsent]: 57,
|
|
118
|
-
[ViableSkill.ResourceLayer]: 60,
|
|
119
|
-
[ViableSkill.ResourceResults]: 62,
|
|
120
|
-
[ViableSkill.ResourceMigrations]: 64,
|
|
121
|
-
[ViableSkill.OwlMeansServer]: 70,
|
|
122
|
-
[ViableSkill.OwlmeansContext]: 65,
|
|
123
|
-
[ViableSkill.OwlMeansServices]: 66,
|
|
124
|
-
[ViableSkill.FixerHeuristics]: 75,
|
|
125
|
-
[ViableSkill.ProjectLayout]: 5,
|
|
126
|
-
[ViableSkill.AgentTooling]: 8,
|
|
127
|
-
[ViableSkill.QueueDiscipline]: 67,
|
|
128
|
-
[ViableSkill.WorkerJobs]: 68,
|
|
129
|
-
[ViableSkill.TargetAgents]: 69,
|
|
130
|
-
[ViableSkill.AgenticChoice]: 71,
|
|
131
|
-
[ViableSkill.TargetLlm]: 72,
|
|
132
|
-
[ViableSkill.TargetAgentTools]: 73,
|
|
133
|
-
[ViableSkill.GameDesign]: 45,
|
|
134
|
-
[ViableSkill.GameScene]: 46,
|
|
135
|
-
[ViableSkill.GameUi]: 47,
|
|
136
|
-
[ViableSkill.GameNetworking]: 48,
|
|
137
|
-
[ViableSkill.LayoutDefinition]: 80,
|
|
138
|
-
[ViableSkill.ScreenDefinition]: 82,
|
|
139
|
-
[ViableSkill.LibrariesCommon]: 90,
|
|
140
|
-
[ViableSkill.LibrariesUiState]: 90,
|
|
141
|
-
[ViableSkill.LibrariesUi]: 90,
|
|
142
|
-
[ViableSkill.LibrariesBackend]: 90,
|
|
143
|
-
[ViableSkill.LibrariesAi]: 90,
|
|
144
|
-
[ViableSkill.LibrariesGame]: 90,
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* The catalogue entries whose subject a PUBLISHED `@owlmeans` package already documents.
|
|
149
|
-
*
|
|
150
|
-
* The catalogue and `agent-meta/skills/*` state the same contract twice — the P1 duplication —
|
|
151
|
-
* and the duplication is not symmetric: a package skill is versioned with the package and reaches
|
|
152
|
-
* a prompt through `owlmeansPackagesPlugin` whenever the request names that package, while a
|
|
153
|
-
* catalogue entry is a constant here that has to be edited by hand every time the framework moves.
|
|
154
|
-
*
|
|
155
|
-
* Naming the overlap is what makes it a DATA decision instead of a code one. A blueprint lists
|
|
156
|
-
* which of these its prompts carry, and a persona's skills are filtered against that list — so
|
|
157
|
-
* dropping one is a line in a blueprint, and restoring it is the same line. Everything NOT in this
|
|
158
|
-
* set is viable's own (output discipline, scope, the view-model convention, the fixer heuristics,
|
|
159
|
-
* where files live) and always loads, because no published package documents it.
|
|
160
|
-
*
|
|
161
|
-
* The default blueprint currently lists every one of them: the reduction is a real change to what
|
|
162
|
-
* a coder is told, and it is measured against a green end-to-end baseline rather than guessed.
|
|
163
|
-
*/
|
|
164
|
-
export const FRAMEWORK_OWNED_SKILLS: ViableSkill[] = [
|
|
165
|
-
ViableSkill.ResourceLayer,
|
|
166
|
-
ViableSkill.ResourceResults,
|
|
167
|
-
ViableSkill.ResourceMigrations,
|
|
168
|
-
ViableSkill.OwlmeansContext,
|
|
169
|
-
ViableSkill.OwlMeansServices,
|
|
170
|
-
ViableSkill.OwlMeansServer,
|
|
171
|
-
ViableSkill.OwlMeansEntrypoints,
|
|
172
|
-
ViableSkill.OwlMeansNav,
|
|
173
|
-
ViableSkill.PermissionModel,
|
|
174
|
-
ViableSkill.OwlmeansState,
|
|
175
|
-
ViableSkill.StoreAccess,
|
|
176
|
-
ViableSkill.ShadcnUi,
|
|
177
|
-
ViableSkill.ReactComponents,
|
|
178
|
-
ViableSkill.FormFeedback,
|
|
179
|
-
]
|
|
180
|
-
|
|
181
|
-
const FRAMEWORK_OWNED = new Set<ViableSkill>(FRAMEWORK_OWNED_SKILLS)
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Filter a persona's skills against what a blueprint asks for.
|
|
185
|
-
*
|
|
186
|
-
* A skill nobody owns always survives. A framework-owned one survives only while the blueprint
|
|
187
|
-
* lists it — so a blueprint built on another persistence layer stops teaching the Postgres
|
|
188
|
-
* resource contract without anybody editing a persona, and a blueprint that lists nothing at all
|
|
189
|
-
* (the shape a test builds) is read as "everything", because an empty allow-list almost always
|
|
190
|
-
* means "not stated" rather than "none".
|
|
191
|
-
*/
|
|
192
|
-
export const skillsForBlueprint = (
|
|
193
|
-
skills: ViableSkill[], allowed?: ViableSkill[]
|
|
194
|
-
): ViableSkill[] => {
|
|
195
|
-
if (allowed == null || allowed.length === 0) return skills
|
|
196
|
-
const permitted = new Set(allowed)
|
|
197
|
-
|
|
198
|
-
return skills.filter(skill => !FRAMEWORK_OWNED.has(skill) || permitted.has(skill))
|
|
199
|
-
}
|
package/src/skills/index.ts
CHANGED