markform 0.1.1 → 0.1.3
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/DOCS.md +546 -0
- package/README.md +340 -71
- package/SPEC.md +2779 -0
- package/dist/ai-sdk.d.mts +2 -2
- package/dist/ai-sdk.mjs +5 -3
- package/dist/{apply-BQdd-fdx.mjs → apply-00UmzDKL.mjs} +849 -730
- package/dist/bin.mjs +6 -3
- package/dist/{cli-pjOiHgCW.mjs → cli-D--Lel-e.mjs} +1374 -428
- package/dist/cli.mjs +6 -3
- package/dist/{coreTypes--6etkcwb.d.mts → coreTypes-BXhhz9Iq.d.mts} +1946 -794
- package/dist/coreTypes-Dful87E0.mjs +537 -0
- package/dist/index.d.mts +116 -19
- package/dist/index.mjs +5 -3
- package/dist/session-Bqnwi9wp.mjs +110 -0
- package/dist/session-DdAtY2Ni.mjs +4 -0
- package/dist/shared-D7gf27Tr.mjs +3 -0
- package/dist/shared-N_s1M-_K.mjs +176 -0
- package/dist/src-Dm8jZ5dl.mjs +7587 -0
- package/examples/celebrity-deep-research/celebrity-deep-research.form.md +912 -0
- package/examples/earnings-analysis/earnings-analysis.form.md +6 -1
- package/examples/earnings-analysis/earnings-analysis.valid.ts +119 -59
- package/examples/movie-research/movie-research-basic.form.md +164 -0
- package/examples/movie-research/movie-research-deep.form.md +486 -0
- package/examples/movie-research/movie-research-minimal.form.md +54 -0
- package/examples/simple/simple-mock-filled.form.md +17 -13
- package/examples/simple/simple-skipped-filled.form.md +32 -9
- package/examples/simple/simple-with-skips.session.yaml +102 -143
- package/examples/simple/simple.form.md +13 -13
- package/examples/simple/simple.session.yaml +80 -69
- package/examples/startup-deep-research/startup-deep-research.form.md +60 -8
- package/examples/startup-research/startup-research-mock-filled.form.md +1 -1
- package/examples/startup-research/startup-research.form.md +1 -1
- package/package.json +10 -14
- package/dist/src-Cs4_9lWP.mjs +0 -2151
- package/examples/political-research/political-research.form.md +0 -233
- package/examples/political-research/political-research.mock.lincoln.form.md +0 -355
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
markform:
|
|
3
|
-
|
|
3
|
+
spec: MF/0.1
|
|
4
|
+
title: Company Quarterly Analysis
|
|
5
|
+
description: Financial analysis with one user field (company) and agent-filled quarterly analysis sections.
|
|
4
6
|
roles:
|
|
5
7
|
- user
|
|
6
8
|
- agent
|
|
7
9
|
role_instructions:
|
|
8
10
|
user: "Enter company identification (legal name, ticker, fiscal year) and the quarter you want analyzed."
|
|
9
11
|
agent: "Complete the company profile and quarterly analysis based on the provided company context."
|
|
12
|
+
harness_config:
|
|
13
|
+
max_issues_per_turn: 5
|
|
14
|
+
max_patches_per_turn: 10
|
|
10
15
|
---
|
|
11
16
|
|
|
12
17
|
{% form id="company_analysis" title="Company Quarterly Analysis Worksheet" %}
|
|
@@ -57,7 +57,14 @@ function getSingleSelectValue(values: Record<string, unknown>, fieldId: string):
|
|
|
57
57
|
function minWords(ctx: ValidatorContext): ValidationIssue[] {
|
|
58
58
|
const min = ctx.params.min as number;
|
|
59
59
|
if (typeof min !== 'number') {
|
|
60
|
-
return [
|
|
60
|
+
return [
|
|
61
|
+
{
|
|
62
|
+
severity: 'error',
|
|
63
|
+
message: 'min_words requires "min" parameter',
|
|
64
|
+
ref: ctx.targetId,
|
|
65
|
+
source: 'code',
|
|
66
|
+
},
|
|
67
|
+
];
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
const value = getStringValue(ctx.values, ctx.targetId);
|
|
@@ -65,12 +72,14 @@ function minWords(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
65
72
|
|
|
66
73
|
const wordCount = countWords(value);
|
|
67
74
|
if (wordCount < min) {
|
|
68
|
-
return [
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
severity: 'error',
|
|
78
|
+
message: `Field requires at least ${min} words (currently ${wordCount})`,
|
|
79
|
+
ref: ctx.targetId,
|
|
80
|
+
source: 'code',
|
|
81
|
+
},
|
|
82
|
+
];
|
|
74
83
|
}
|
|
75
84
|
return [];
|
|
76
85
|
}
|
|
@@ -82,7 +91,14 @@ function minWords(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
82
91
|
function maxWords(ctx: ValidatorContext): ValidationIssue[] {
|
|
83
92
|
const max = ctx.params.max as number;
|
|
84
93
|
if (typeof max !== 'number') {
|
|
85
|
-
return [
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
severity: 'error',
|
|
97
|
+
message: 'max_words requires "max" parameter',
|
|
98
|
+
ref: ctx.targetId,
|
|
99
|
+
source: 'code',
|
|
100
|
+
},
|
|
101
|
+
];
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
const value = getStringValue(ctx.values, ctx.targetId);
|
|
@@ -90,12 +106,14 @@ function maxWords(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
90
106
|
|
|
91
107
|
const wordCount = countWords(value);
|
|
92
108
|
if (wordCount > max) {
|
|
93
|
-
return [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
109
|
+
return [
|
|
110
|
+
{
|
|
111
|
+
severity: 'warning',
|
|
112
|
+
message: `Field exceeds ${max} word limit (currently ${wordCount})`,
|
|
113
|
+
ref: ctx.targetId,
|
|
114
|
+
source: 'code',
|
|
115
|
+
},
|
|
116
|
+
];
|
|
99
117
|
}
|
|
100
118
|
return [];
|
|
101
119
|
}
|
|
@@ -110,22 +128,31 @@ function sumTo(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
110
128
|
const tolerance = (ctx.params.tolerance as number) ?? 0.1;
|
|
111
129
|
|
|
112
130
|
if (!Array.isArray(fields)) {
|
|
113
|
-
return [
|
|
131
|
+
return [
|
|
132
|
+
{
|
|
133
|
+
severity: 'error',
|
|
134
|
+
message: 'sum_to requires "fields" array parameter',
|
|
135
|
+
ref: ctx.targetId,
|
|
136
|
+
source: 'code',
|
|
137
|
+
},
|
|
138
|
+
];
|
|
114
139
|
}
|
|
115
140
|
|
|
116
|
-
const values = fields.map(fieldId => getNumberValue(ctx.values, fieldId) ?? 0);
|
|
141
|
+
const values = fields.map((fieldId) => getNumberValue(ctx.values, fieldId) ?? 0);
|
|
117
142
|
const sum = values.reduce((a, b) => a + b, 0);
|
|
118
143
|
|
|
119
144
|
// Only validate if at least one value is set
|
|
120
|
-
if (values.every(v => v === 0)) return [];
|
|
145
|
+
if (values.every((v) => v === 0)) return [];
|
|
121
146
|
|
|
122
147
|
if (Math.abs(sum - target) > tolerance) {
|
|
123
|
-
return [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
return [
|
|
149
|
+
{
|
|
150
|
+
severity: 'error',
|
|
151
|
+
message: `Fields must sum to ${target}% (currently ${sum.toFixed(1)}%)`,
|
|
152
|
+
ref: fields[0],
|
|
153
|
+
source: 'code',
|
|
154
|
+
},
|
|
155
|
+
];
|
|
129
156
|
}
|
|
130
157
|
return [];
|
|
131
158
|
}
|
|
@@ -140,25 +167,31 @@ function sumToPercentList(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
140
167
|
|
|
141
168
|
if (items.length === 0) return [];
|
|
142
169
|
|
|
143
|
-
const percentages = items
|
|
170
|
+
const percentages = items
|
|
171
|
+
.map((item) => extractPercentage(item))
|
|
172
|
+
.filter((p): p is number => p !== null);
|
|
144
173
|
|
|
145
174
|
if (percentages.length === 0) {
|
|
146
|
-
return [
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
return [
|
|
176
|
+
{
|
|
177
|
+
severity: 'warning',
|
|
178
|
+
message: 'Items should include percentages (format: "Label: XX%")',
|
|
179
|
+
ref: ctx.targetId,
|
|
180
|
+
source: 'code',
|
|
181
|
+
},
|
|
182
|
+
];
|
|
152
183
|
}
|
|
153
184
|
|
|
154
185
|
const sum = percentages.reduce((a, b) => a + b, 0);
|
|
155
186
|
if (Math.abs(sum - target) > 0.1) {
|
|
156
|
-
return [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
187
|
+
return [
|
|
188
|
+
{
|
|
189
|
+
severity: 'warning',
|
|
190
|
+
message: `Items should sum to ${target}% (currently ${sum.toFixed(1)}%)`,
|
|
191
|
+
ref: ctx.targetId,
|
|
192
|
+
source: 'code',
|
|
193
|
+
},
|
|
194
|
+
];
|
|
162
195
|
}
|
|
163
196
|
return [];
|
|
164
197
|
}
|
|
@@ -172,7 +205,14 @@ function requiredIf(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
172
205
|
const targetField = (ctx.params.then as string) ?? ctx.targetId;
|
|
173
206
|
|
|
174
207
|
if (!triggerField) {
|
|
175
|
-
return [
|
|
208
|
+
return [
|
|
209
|
+
{
|
|
210
|
+
severity: 'error',
|
|
211
|
+
message: 'required_if requires "when" parameter',
|
|
212
|
+
ref: ctx.targetId,
|
|
213
|
+
source: 'code',
|
|
214
|
+
},
|
|
215
|
+
];
|
|
176
216
|
}
|
|
177
217
|
|
|
178
218
|
const trigger = ctx.values[triggerField] as Record<string, unknown> | undefined;
|
|
@@ -189,12 +229,14 @@ function requiredIf(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
189
229
|
(target.kind === 'number' && target.value == null);
|
|
190
230
|
|
|
191
231
|
if (triggerHasValue && targetEmpty) {
|
|
192
|
-
return [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
return [
|
|
233
|
+
{
|
|
234
|
+
severity: 'error',
|
|
235
|
+
message: `This field is required when ${triggerField} has a value`,
|
|
236
|
+
ref: targetField,
|
|
237
|
+
source: 'code',
|
|
238
|
+
},
|
|
239
|
+
];
|
|
198
240
|
}
|
|
199
241
|
return [];
|
|
200
242
|
}
|
|
@@ -209,19 +251,28 @@ function requiredIfEquals(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
209
251
|
const targetField = (ctx.params.then as string) ?? ctx.targetId;
|
|
210
252
|
|
|
211
253
|
if (!triggerField || expectedValue === undefined) {
|
|
212
|
-
return [
|
|
254
|
+
return [
|
|
255
|
+
{
|
|
256
|
+
severity: 'error',
|
|
257
|
+
message: 'required_if_equals requires "when" and "equals" parameters',
|
|
258
|
+
ref: ctx.targetId,
|
|
259
|
+
source: 'code',
|
|
260
|
+
},
|
|
261
|
+
];
|
|
213
262
|
}
|
|
214
263
|
|
|
215
264
|
const triggerValue = getSingleSelectValue(ctx.values, triggerField);
|
|
216
265
|
const target = getStringValue(ctx.values, targetField);
|
|
217
266
|
|
|
218
267
|
if (triggerValue === expectedValue && (!target || target.trim().length === 0)) {
|
|
219
|
-
return [
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
268
|
+
return [
|
|
269
|
+
{
|
|
270
|
+
severity: 'error',
|
|
271
|
+
message: `This field is required when ${triggerField} is "${expectedValue}"`,
|
|
272
|
+
ref: targetField,
|
|
273
|
+
source: 'code',
|
|
274
|
+
},
|
|
275
|
+
];
|
|
225
276
|
}
|
|
226
277
|
return [];
|
|
227
278
|
}
|
|
@@ -232,26 +283,35 @@ function requiredIfEquals(ctx: ValidatorContext): ValidationIssue[] {
|
|
|
232
283
|
*/
|
|
233
284
|
function itemFormat(ctx: ValidatorContext): ValidationIssue[] {
|
|
234
285
|
const pattern = ctx.params.pattern as string;
|
|
235
|
-
const example = ctx.params.example as string ?? '';
|
|
286
|
+
const example = (ctx.params.example as string) ?? '';
|
|
236
287
|
|
|
237
288
|
if (!pattern) {
|
|
238
|
-
return [
|
|
289
|
+
return [
|
|
290
|
+
{
|
|
291
|
+
severity: 'error',
|
|
292
|
+
message: 'item_format requires "pattern" parameter',
|
|
293
|
+
ref: ctx.targetId,
|
|
294
|
+
source: 'code',
|
|
295
|
+
},
|
|
296
|
+
];
|
|
239
297
|
}
|
|
240
298
|
|
|
241
299
|
const items = getStringListItems(ctx.values, ctx.targetId);
|
|
242
300
|
if (items.length === 0) return [];
|
|
243
301
|
|
|
244
302
|
const regex = new RegExp(pattern);
|
|
245
|
-
const malformed = items.filter(item => !regex.test(item));
|
|
303
|
+
const malformed = items.filter((item) => !regex.test(item));
|
|
246
304
|
|
|
247
305
|
if (malformed.length > 0) {
|
|
248
306
|
const hint = example ? ` Expected format: "${example}"` : '';
|
|
249
|
-
return [
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
307
|
+
return [
|
|
308
|
+
{
|
|
309
|
+
severity: 'warning',
|
|
310
|
+
message: `${malformed.length} item(s) don't match expected format.${hint}`,
|
|
311
|
+
ref: ctx.targetId,
|
|
312
|
+
source: 'code',
|
|
313
|
+
},
|
|
314
|
+
];
|
|
255
315
|
}
|
|
256
316
|
return [];
|
|
257
317
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
markform:
|
|
3
|
+
spec: MF/0.1
|
|
4
|
+
title: Movie Research (Basic)
|
|
5
|
+
description: Standard movie research form pulling ratings and key stats from IMDB, Rotten Tomatoes, and Metacritic.
|
|
6
|
+
roles:
|
|
7
|
+
- user
|
|
8
|
+
- agent
|
|
9
|
+
role_instructions:
|
|
10
|
+
user: "Enter the movie title and optionally the year for disambiguation."
|
|
11
|
+
agent: |
|
|
12
|
+
Research and fill in all fields for the specified movie.
|
|
13
|
+
Guidelines:
|
|
14
|
+
1. WORKFLOW - Complete sections in order:
|
|
15
|
+
- First identify the movie title
|
|
16
|
+
- Then find all source URLs (verify you have the right movie on each site)
|
|
17
|
+
- Then fill in details (year, directors, ratings) from those sources
|
|
18
|
+
2. PRIMARY SOURCES:
|
|
19
|
+
- IMDB (imdb.com) for ratings, runtime, and technical details
|
|
20
|
+
- Rotten Tomatoes (rottentomatoes.com) for Tomatometer and Audience Score
|
|
21
|
+
- Metacritic (metacritic.com) for Metascore
|
|
22
|
+
3. Use the EXACT numeric scores from each source - don't average or interpret
|
|
23
|
+
4. Skip fields if any scores are unavailable (older films may lack some metrics)
|
|
24
|
+
5. Use the tool to add notes if there is missing, confusing, or unclear information
|
|
25
|
+
in the values you provide.
|
|
26
|
+
harness_config:
|
|
27
|
+
max_issues_per_turn: 3
|
|
28
|
+
max_patches_per_turn: 8
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
{% form id="movie_research_basic" title="Movie Research (Basic)" %}
|
|
32
|
+
|
|
33
|
+
{% description ref="movie_research_basic" %}
|
|
34
|
+
Standard research form for gathering ratings and key statistics for any film. Pulls from IMDB, Rotten Tomatoes, and Metacritic.
|
|
35
|
+
{% /description %}
|
|
36
|
+
|
|
37
|
+
{% field-group id="movie_input" title="Movie Identification" %}
|
|
38
|
+
|
|
39
|
+
{% string-field id="movie" label="Movie" role="user" required=true minLength=1 maxLength=300 %}{% /string-field %}
|
|
40
|
+
|
|
41
|
+
{% instructions ref="movie" %}
|
|
42
|
+
Enter the movie title (add any details to help identify, like "Barbie 2023" or "the Batman movie with Robert Pattinson")
|
|
43
|
+
{% /instructions %}
|
|
44
|
+
|
|
45
|
+
{% /field-group %}
|
|
46
|
+
|
|
47
|
+
{% field-group id="title_identification" title="Title Identification" %}
|
|
48
|
+
|
|
49
|
+
{% string-field id="full_title" label="Full Title" role="agent" required=true %}{% /string-field %}
|
|
50
|
+
|
|
51
|
+
{% instructions ref="full_title" %}
|
|
52
|
+
Look up what film the user had in mind and fill in the official title including subtitle if any (e.g., "The Lord of the Rings: The Fellowship of the Ring").
|
|
53
|
+
{% /instructions %}
|
|
54
|
+
|
|
55
|
+
{% /field-group %}
|
|
56
|
+
|
|
57
|
+
{% field-group id="sources" title="Sources" %}
|
|
58
|
+
|
|
59
|
+
{% url-field id="imdb_url" label="IMDB URL" role="agent" required=true %}{% /url-field %}
|
|
60
|
+
|
|
61
|
+
{% instructions ref="imdb_url" %}
|
|
62
|
+
Direct link to the movie's IMDB page (e.g., https://www.imdb.com/title/tt0111161/).
|
|
63
|
+
{% /instructions %}
|
|
64
|
+
|
|
65
|
+
{% url-field id="rt_url" label="Rotten Tomatoes URL" role="agent" %}{% /url-field %}
|
|
66
|
+
|
|
67
|
+
{% instructions ref="rt_url" %}
|
|
68
|
+
Direct link to the movie's Rotten Tomatoes page.
|
|
69
|
+
{% /instructions %}
|
|
70
|
+
|
|
71
|
+
{% url-field id="metacritic_url" label="Metacritic URL" role="agent" %}{% /url-field %}
|
|
72
|
+
|
|
73
|
+
{% instructions ref="metacritic_url" %}
|
|
74
|
+
Direct link to the movie's Metacritic page.
|
|
75
|
+
{% /instructions %}
|
|
76
|
+
|
|
77
|
+
{% /field-group %}
|
|
78
|
+
|
|
79
|
+
{% field-group id="basic_details" title="Basic Details" %}
|
|
80
|
+
|
|
81
|
+
{% number-field id="year" label="Release Year" role="agent" required=true min=1888 max=2030 %}{% /number-field %}
|
|
82
|
+
|
|
83
|
+
{% string-list id="directors" label="Director(s)" role="agent" required=true %}{% /string-list %}
|
|
84
|
+
|
|
85
|
+
{% instructions ref="directors" %}
|
|
86
|
+
One director per line. Most films have one; some have two or more co-directors.
|
|
87
|
+
{% /instructions %}
|
|
88
|
+
|
|
89
|
+
{% number-field id="runtime_minutes" label="Runtime (minutes)" role="agent" min=1 max=1000 %}{% /number-field %}
|
|
90
|
+
|
|
91
|
+
{% single-select id="mpaa_rating" label="MPAA Rating" role="agent" %}
|
|
92
|
+
- [ ] G {% #g %}
|
|
93
|
+
- [ ] PG {% #pg %}
|
|
94
|
+
- [ ] PG-13 {% #pg_13 %}
|
|
95
|
+
- [ ] R {% #r %}
|
|
96
|
+
- [ ] NC-17 {% #nc_17 %}
|
|
97
|
+
- [ ] NR/Unrated {% #nr %}
|
|
98
|
+
{% /single-select %}
|
|
99
|
+
|
|
100
|
+
{% /field-group %}
|
|
101
|
+
|
|
102
|
+
{% field-group id="imdb_ratings" title="IMDB Ratings" %}
|
|
103
|
+
|
|
104
|
+
{% number-field id="imdb_rating" label="IMDB Rating" role="agent" min=1.0 max=10.0 %}{% /number-field %}
|
|
105
|
+
|
|
106
|
+
{% instructions ref="imdb_rating" %}
|
|
107
|
+
IMDB user rating (1.0-10.0 scale).
|
|
108
|
+
{% /instructions %}
|
|
109
|
+
|
|
110
|
+
{% number-field id="imdb_votes" label="IMDB Vote Count" role="agent" min=0 %}{% /number-field %}
|
|
111
|
+
|
|
112
|
+
{% instructions ref="imdb_votes" %}
|
|
113
|
+
Number of IMDB user votes (e.g., 2800000 for a popular film).
|
|
114
|
+
{% /instructions %}
|
|
115
|
+
|
|
116
|
+
{% /field-group %}
|
|
117
|
+
|
|
118
|
+
{% field-group id="rotten_tomatoes_ratings" title="Rotten Tomatoes Ratings" %}
|
|
119
|
+
|
|
120
|
+
{% number-field id="rt_critics_score" label="Tomatometer (Critics)" role="agent" min=0 max=100 %}{% /number-field %}
|
|
121
|
+
|
|
122
|
+
{% instructions ref="rt_critics_score" %}
|
|
123
|
+
Tomatometer percentage (0-100).
|
|
124
|
+
{% /instructions %}
|
|
125
|
+
|
|
126
|
+
{% number-field id="rt_critics_count" label="Critics Review Count" role="agent" min=0 %}{% /number-field %}
|
|
127
|
+
|
|
128
|
+
{% number-field id="rt_audience_score" label="Audience Score" role="agent" min=0 max=100 %}{% /number-field %}
|
|
129
|
+
|
|
130
|
+
{% instructions ref="rt_audience_score" %}
|
|
131
|
+
Audience Score percentage (0-100).
|
|
132
|
+
{% /instructions %}
|
|
133
|
+
|
|
134
|
+
{% /field-group %}
|
|
135
|
+
|
|
136
|
+
{% field-group id="metacritic_ratings" title="Metacritic Ratings" %}
|
|
137
|
+
|
|
138
|
+
{% number-field id="metacritic_score" label="Metacritic Score" role="agent" min=0 max=100 %}{% /number-field %}
|
|
139
|
+
|
|
140
|
+
{% instructions ref="metacritic_score" %}
|
|
141
|
+
Metascore (0-100 scale). Leave empty if not available.
|
|
142
|
+
{% /instructions %}
|
|
143
|
+
|
|
144
|
+
{% /field-group %}
|
|
145
|
+
|
|
146
|
+
{% field-group id="summary" title="Summary" %}
|
|
147
|
+
|
|
148
|
+
{% string-field id="logline" label="One-Line Summary" role="agent" maxLength=300 %}{% /string-field %}
|
|
149
|
+
|
|
150
|
+
{% instructions ref="logline" %}
|
|
151
|
+
Brief plot summary in 1-2 sentences, no spoilers.
|
|
152
|
+
{% /instructions %}
|
|
153
|
+
|
|
154
|
+
{% string-list id="notable_awards" label="Notable Awards" role="agent" %}{% /string-list %}
|
|
155
|
+
|
|
156
|
+
{% instructions ref="notable_awards" %}
|
|
157
|
+
Major awards won. One per line.
|
|
158
|
+
Format: Award | Category | Year
|
|
159
|
+
Example: "Oscar | Best Picture | 1995"
|
|
160
|
+
{% /instructions %}
|
|
161
|
+
|
|
162
|
+
{% /field-group %}
|
|
163
|
+
|
|
164
|
+
{% /form %}
|