@forwardimpact/pathway 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/bin/fit-pathway.js +32 -12
  2. package/package.json +3 -3
  3. package/src/commands/build.js +98 -2
  4. package/src/commands/index.js +1 -0
  5. package/src/commands/interview.js +52 -14
  6. package/src/commands/job.js +1 -0
  7. package/src/commands/questions.js +13 -10
  8. package/src/commands/stage.js +8 -8
  9. package/src/commands/update.js +133 -0
  10. package/src/components/command-prompt.js +85 -0
  11. package/src/components/nav.js +2 -2
  12. package/src/components/top-bar.js +97 -0
  13. package/src/css/bundles/app.css +2 -0
  14. package/src/css/components/badges.css +41 -11
  15. package/src/css/components/command-prompt.css +98 -0
  16. package/src/css/components/layout.css +0 -3
  17. package/src/css/components/nav.css +121 -81
  18. package/src/css/components/surfaces.css +1 -1
  19. package/src/css/components/top-bar.css +180 -0
  20. package/src/css/pages/agent-builder.css +0 -9
  21. package/src/css/pages/landing.css +4 -0
  22. package/src/css/pages/lifecycle.css +5 -2
  23. package/src/css/reset.css +1 -1
  24. package/src/css/tokens.css +25 -11
  25. package/src/css/views/slide-base.css +2 -1
  26. package/src/formatters/agent/dom.js +0 -26
  27. package/src/formatters/agent/profile.js +13 -7
  28. package/src/formatters/agent/skill.js +4 -4
  29. package/src/formatters/interview/markdown.js +62 -3
  30. package/src/formatters/interview/shared.js +89 -52
  31. package/src/formatters/questions/markdown.js +15 -0
  32. package/src/formatters/questions/shared.js +70 -58
  33. package/src/formatters/stage/dom.js +13 -10
  34. package/src/formatters/stage/microdata.js +14 -8
  35. package/src/formatters/stage/shared.js +4 -4
  36. package/src/index.html +69 -44
  37. package/src/lib/cli-command.js +145 -0
  38. package/src/lib/state.js +2 -0
  39. package/src/lib/yaml-loader.js +39 -21
  40. package/src/main.js +47 -26
  41. package/src/pages/agent-builder.js +0 -28
  42. package/src/pages/behaviour.js +3 -1
  43. package/src/pages/discipline.js +3 -1
  44. package/src/pages/driver.js +6 -1
  45. package/src/pages/grade.js +6 -1
  46. package/src/pages/interview.js +61 -5
  47. package/src/pages/job.js +1 -0
  48. package/src/pages/landing.js +7 -0
  49. package/src/pages/skill.js +9 -2
  50. package/src/pages/track.js +6 -1
  51. package/src/slides/job.js +1 -0
  52. package/templates/agent.template.md +17 -10
  53. package/templates/install.template.sh +33 -0
  54. package/templates/skill.template.md +15 -7
@@ -10,9 +10,9 @@ import {
10
10
  getDisciplineSkillIds,
11
11
  } from "@forwardimpact/model/derivation";
12
12
  import {
13
- deriveInterviewQuestions,
14
- deriveShortInterview,
15
- deriveBehaviourQuestions,
13
+ deriveMissionFitInterview,
14
+ deriveDecompositionInterview,
15
+ deriveStakeholderInterview,
16
16
  } from "@forwardimpact/model/interview";
17
17
  import { getOrCreateJob } from "@forwardimpact/model/job-cache";
18
18
 
@@ -20,26 +20,35 @@ import { getOrCreateJob } from "@forwardimpact/model/job-cache";
20
20
  * Interview type configurations
21
21
  */
22
22
  export const INTERVIEW_TYPES = {
23
- short: {
24
- id: "short",
25
- name: "Screening",
26
- description: "20-minute screening interview",
27
- icon: "⏱️",
28
- expectedDurationMinutes: 20,
29
- },
30
- behaviour: {
31
- id: "behaviour",
32
- name: "Behavioural",
33
- description: "Focus on behaviours and mindsets",
34
- icon: "🧠",
23
+ mission: {
24
+ id: "mission",
25
+ name: "Mission Fit",
26
+ description:
27
+ "Assess technical skills and alignment with role expectations. Focus on depth of knowledge, problem-solving approach, and ability to articulate technical decisions.",
28
+ icon: "🎯",
35
29
  expectedDurationMinutes: 45,
30
+ panel: "Recruiting Manager + 1 Senior Engineer",
31
+ questionTypes: ["skill"],
32
+ },
33
+ decomposition: {
34
+ id: "decomposition",
35
+ name: "Decomposition",
36
+ description:
37
+ "Evaluate how candidates break down ambiguous problems into actionable components. Inspired by Palantir's technique—focus on structured thinking, trade-off analysis, and communication under uncertainty.",
38
+ icon: "🧩",
39
+ expectedDurationMinutes: 60,
40
+ panel: "2 Senior Engineers",
41
+ questionTypes: ["capability"],
36
42
  },
37
- full: {
38
- id: "full",
39
- name: "Full Interview",
40
- description: "Comprehensive interview covering all skills and behaviours",
41
- icon: "📋",
42
- expectedDurationMinutes: 90,
43
+ stakeholder: {
44
+ id: "stakeholder",
45
+ name: "Stakeholder Simulation",
46
+ description:
47
+ "Simulate real-world stakeholder interactions through behaviour-focused assessment. Focus on communication style, influence, and ability to navigate competing priorities.",
48
+ icon: "👥",
49
+ expectedDurationMinutes: 60,
50
+ panel: "3-4 Stakeholders",
51
+ questionTypes: ["behaviour"],
43
52
  },
44
53
  };
45
54
 
@@ -67,14 +76,27 @@ function groupQuestionsIntoSections(questions) {
67
76
  };
68
77
  }
69
78
 
70
- sections[id].questions.push({
79
+ const questionEntry = {
71
80
  skillOrBehaviourId: id,
72
81
  skillOrBehaviourName: name,
73
82
  type,
74
83
  level,
75
84
  question: q.question.text,
76
85
  followUps: q.question.followUps || [],
77
- });
86
+ lookingFor: q.question.lookingFor || [],
87
+ };
88
+
89
+ if (q.question.decompositionPrompts) {
90
+ questionEntry.decompositionPrompts = q.question.decompositionPrompts;
91
+ }
92
+ if (q.question.simulationPrompts) {
93
+ questionEntry.simulationPrompts = q.question.simulationPrompts;
94
+ }
95
+ if (q.question.context) {
96
+ questionEntry.context = q.question.context;
97
+ }
98
+
99
+ sections[id].questions.push(questionEntry);
78
100
  }
79
101
 
80
102
  return Object.values(sections);
@@ -83,7 +105,7 @@ function groupQuestionsIntoSections(questions) {
83
105
  /**
84
106
  * @typedef {Object} InterviewDetailView
85
107
  * @property {string} title
86
- * @property {string} interviewType - 'full', 'short', or 'behaviour'
108
+ * @property {string} interviewType - 'mission', 'decomposition', or 'stakeholder'
87
109
  * @property {string} disciplineId
88
110
  * @property {string} disciplineName
89
111
  * @property {string} gradeId
@@ -104,7 +126,7 @@ function groupQuestionsIntoSections(questions) {
104
126
  * @param {Array} params.skills
105
127
  * @param {Array} params.behaviours
106
128
  * @param {Array} params.questions
107
- * @param {string} [params.interviewType='full']
129
+ * @param {string} [params.interviewType='mission']
108
130
  * @returns {InterviewDetailView|null}
109
131
  */
110
132
  export function prepareInterviewDetail({
@@ -114,7 +136,7 @@ export function prepareInterviewDetail({
114
136
  skills,
115
137
  behaviours,
116
138
  questions,
117
- interviewType = "full",
139
+ interviewType = "mission",
118
140
  }) {
119
141
  if (!discipline || !grade) return null;
120
142
 
@@ -130,18 +152,26 @@ export function prepareInterviewDetail({
130
152
 
131
153
  let interviewGuide;
132
154
  switch (interviewType) {
133
- case "short":
134
- interviewGuide = deriveShortInterview({ job, questionBank: questions });
155
+ case "mission":
156
+ interviewGuide = deriveMissionFitInterview({
157
+ job,
158
+ questionBank: questions,
159
+ });
135
160
  break;
136
- case "behaviour":
137
- interviewGuide = deriveBehaviourQuestions({
161
+ case "decomposition":
162
+ interviewGuide = deriveDecompositionInterview({
163
+ job,
164
+ questionBank: questions,
165
+ });
166
+ break;
167
+ case "stakeholder":
168
+ interviewGuide = deriveStakeholderInterview({
138
169
  job,
139
170
  questionBank: questions,
140
171
  });
141
172
  break;
142
- case "full":
143
173
  default:
144
- interviewGuide = deriveInterviewQuestions({
174
+ interviewGuide = deriveMissionFitInterview({
145
175
  job,
146
176
  questionBank: questions,
147
177
  });
@@ -151,19 +181,27 @@ export function prepareInterviewDetail({
151
181
  // Extract the questions array from the interview guide
152
182
  const rawQuestions = interviewGuide.questions || [];
153
183
 
154
- // Separate skill and behaviour questions based on targetType
184
+ // Separate questions by type
155
185
  const skillQuestions = rawQuestions.filter((q) => q.targetType === "skill");
156
186
  const behaviourQuestions = rawQuestions.filter(
157
187
  (q) => q.targetType === "behaviour",
158
188
  );
189
+ const capabilityQuestions = rawQuestions.filter(
190
+ (q) => q.targetType === "capability",
191
+ );
159
192
 
160
193
  const skillSections = groupQuestionsIntoSections(skillQuestions);
161
194
  const behaviourSections = groupQuestionsIntoSections(behaviourQuestions);
195
+ const capabilitySections = groupQuestionsIntoSections(capabilityQuestions);
162
196
 
163
- const allSections = [...skillSections, ...behaviourSections];
197
+ const allSections = [
198
+ ...skillSections,
199
+ ...behaviourSections,
200
+ ...capabilitySections,
201
+ ];
164
202
  const totalQuestions = rawQuestions.length;
165
203
 
166
- const typeConfig = INTERVIEW_TYPES[interviewType] || INTERVIEW_TYPES.full;
204
+ const typeConfig = INTERVIEW_TYPES[interviewType] || INTERVIEW_TYPES.mission;
167
205
 
168
206
  return {
169
207
  title: job.title,
@@ -293,24 +331,19 @@ export function prepareAllInterviews({
293
331
  if (!job) return null;
294
332
 
295
333
  // Generate all interview types
296
- const shortInterview = deriveShortInterview({
334
+ const missionInterview = deriveMissionFitInterview({
297
335
  job,
298
336
  questionBank: questions,
299
- targetMinutes: 20,
300
337
  });
301
338
 
302
- const behaviourInterview = deriveBehaviourQuestions({
339
+ const decompositionInterview = deriveDecompositionInterview({
303
340
  job,
304
341
  questionBank: questions,
305
342
  });
306
343
 
307
- const fullInterview = deriveInterviewQuestions({
344
+ const stakeholderInterview = deriveStakeholderInterview({
308
345
  job,
309
346
  questionBank: questions,
310
- options: {
311
- targetMinutes: 60,
312
- skillBehaviourRatio: 0.6,
313
- },
314
347
  });
315
348
 
316
349
  return {
@@ -321,17 +354,21 @@ export function prepareAllInterviews({
321
354
  trackId: track?.id || null,
322
355
  trackName: track?.name || null,
323
356
  interviews: {
324
- short: {
325
- ...shortInterview,
326
- type: "short",
327
- typeInfo: INTERVIEW_TYPES.short,
357
+ mission: {
358
+ ...missionInterview,
359
+ type: "mission",
360
+ typeInfo: INTERVIEW_TYPES.mission,
361
+ },
362
+ decomposition: {
363
+ ...decompositionInterview,
364
+ type: "decomposition",
365
+ typeInfo: INTERVIEW_TYPES.decomposition,
328
366
  },
329
- behaviour: {
330
- ...behaviourInterview,
331
- type: "behaviour",
332
- typeInfo: INTERVIEW_TYPES.behaviour,
367
+ stakeholder: {
368
+ ...stakeholderInterview,
369
+ type: "stakeholder",
370
+ typeInfo: INTERVIEW_TYPES.stakeholder,
333
371
  },
334
- full: { ...fullInterview, type: "full", typeInfo: INTERVIEW_TYPES.full },
335
372
  },
336
373
  };
337
374
  }
@@ -259,6 +259,21 @@ function formatSingleSource(view) {
259
259
  for (const q of byLevel[level]) {
260
260
  lines.push(` • [${q.id}] ${q.text}`);
261
261
  lines.push(` Duration: ${q.expectedDurationMinutes} min`);
262
+ if (q.context) {
263
+ lines.push(` Context: ${q.context}`);
264
+ }
265
+ if (q.simulationPrompts && q.simulationPrompts.length > 0) {
266
+ lines.push(" Steer the simulation:");
267
+ for (const prompt of q.simulationPrompts) {
268
+ lines.push(` - ${prompt}`);
269
+ }
270
+ }
271
+ if (q.decompositionPrompts && q.decompositionPrompts.length > 0) {
272
+ lines.push(" Guide candidate thinking:");
273
+ for (const prompt of q.decompositionPrompts) {
274
+ lines.push(` - ${prompt}`);
275
+ }
276
+ }
262
277
  if (q.lookingFor.length > 0) {
263
278
  lines.push(" Looking for:");
264
279
  for (const item of q.lookingFor) {
@@ -46,6 +46,9 @@ export const BEHAVIOUR_MATURITIES = [
46
46
  * @property {string[]} lookingFor - Expected answer indicators
47
47
  * @property {number} expectedDurationMinutes - Time estimate
48
48
  * @property {string[]} [followUps] - Follow-up questions
49
+ * @property {string} [context] - Scenario context
50
+ * @property {string[]} [simulationPrompts] - Simulation steering prompts
51
+ * @property {string[]} [decompositionPrompts] - Decomposition guidance prompts
49
52
  */
50
53
 
51
54
  /**
@@ -103,6 +106,11 @@ function getSkillCapability(skillId, skills) {
103
106
  return skill ? skill.capability : null;
104
107
  }
105
108
 
109
+ /**
110
+ * Role type keys in question YAML files
111
+ */
112
+ const ROLE_TYPES = ["professionalQuestions", "managementQuestions"];
113
+
106
114
  /**
107
115
  * Flatten all questions from question bank
108
116
  * @param {Object} questionBank
@@ -115,78 +123,76 @@ export function flattenQuestions(questionBank, skills, behaviours, filter) {
115
123
  const questions = [];
116
124
 
117
125
  // Process skill questions
118
- for (const [skillId, levels] of Object.entries(
126
+ for (const [skillId, roleTypes] of Object.entries(
119
127
  questionBank.skillLevels || {},
120
128
  )) {
121
129
  const skillName = getSkillName(skillId, skills);
122
130
  const capability = getSkillCapability(skillId, skills);
123
131
 
124
- // Filter by skill IDs
125
132
  if (filter.skills && !filter.skills.includes(skillId)) continue;
126
-
127
- // Skip skills if filtering by specific behaviours
128
133
  if (filter.behaviours) continue;
129
-
130
- // Filter by capability
131
134
  if (filter.capability && capability !== filter.capability) continue;
132
-
133
- for (const [level, levelQuestions] of Object.entries(levels)) {
134
- // Filter by level
135
- if (filter.level && level !== filter.level) continue;
136
-
137
- // Skip if filtering by maturity (behaviour-only filter)
138
- if (filter.maturity) continue;
139
-
140
- for (const q of levelQuestions) {
141
- questions.push({
142
- source: skillId,
143
- sourceName: skillName,
144
- sourceType: "skill",
145
- level,
146
- id: q.id,
147
- text: q.text,
148
- lookingFor: q.lookingFor || [],
149
- expectedDurationMinutes: q.expectedDurationMinutes || 5,
150
- followUps: q.followUps || [],
151
- });
135
+ if (filter.maturity) continue;
136
+
137
+ for (const roleType of ROLE_TYPES) {
138
+ const levels = roleTypes[roleType];
139
+ if (!levels) continue;
140
+
141
+ for (const [level, levelQuestions] of Object.entries(levels)) {
142
+ if (filter.level && level !== filter.level) continue;
143
+
144
+ for (const q of levelQuestions) {
145
+ questions.push({
146
+ source: skillId,
147
+ sourceName: skillName,
148
+ sourceType: "skill",
149
+ level,
150
+ id: q.id,
151
+ text: q.text,
152
+ lookingFor: q.lookingFor || [],
153
+ expectedDurationMinutes: q.expectedDurationMinutes || 5,
154
+ followUps: q.followUps || [],
155
+ context: q.context || null,
156
+ decompositionPrompts: q.decompositionPrompts || [],
157
+ });
158
+ }
152
159
  }
153
160
  }
154
161
  }
155
162
 
156
163
  // Process behaviour questions
157
- for (const [behaviourId, maturities] of Object.entries(
164
+ for (const [behaviourId, roleTypes] of Object.entries(
158
165
  questionBank.behaviourMaturities || {},
159
166
  )) {
160
167
  const behaviourName = getBehaviourName(behaviourId, behaviours);
161
168
 
162
- // Filter by behaviour IDs
163
169
  if (filter.behaviours && !filter.behaviours.includes(behaviourId)) continue;
164
-
165
- // Skip behaviours if filtering by capability (skill-only filter)
166
170
  if (filter.capability) continue;
167
-
168
- // Skip behaviours if filtering by specific skills
169
171
  if (filter.skills) continue;
170
-
171
- for (const [maturity, maturityQuestions] of Object.entries(maturities)) {
172
- // Filter by maturity
173
- if (filter.maturity && maturity !== filter.maturity) continue;
174
-
175
- // Skip if filtering by level (skill-only filter)
176
- if (filter.level) continue;
177
-
178
- for (const q of maturityQuestions) {
179
- questions.push({
180
- source: behaviourId,
181
- sourceName: behaviourName,
182
- sourceType: "behaviour",
183
- level: maturity,
184
- id: q.id,
185
- text: q.text,
186
- lookingFor: q.lookingFor || [],
187
- expectedDurationMinutes: q.expectedDurationMinutes || 5,
188
- followUps: q.followUps || [],
189
- });
172
+ if (filter.level) continue;
173
+
174
+ for (const roleType of ROLE_TYPES) {
175
+ const maturities = roleTypes[roleType];
176
+ if (!maturities) continue;
177
+
178
+ for (const [maturity, maturityQuestions] of Object.entries(maturities)) {
179
+ if (filter.maturity && maturity !== filter.maturity) continue;
180
+
181
+ for (const q of maturityQuestions) {
182
+ questions.push({
183
+ source: behaviourId,
184
+ sourceName: behaviourName,
185
+ sourceType: "behaviour",
186
+ level: maturity,
187
+ id: q.id,
188
+ text: q.text,
189
+ lookingFor: q.lookingFor || [],
190
+ expectedDurationMinutes: q.expectedDurationMinutes || 5,
191
+ followUps: q.followUps || [],
192
+ context: q.context || null,
193
+ simulationPrompts: q.simulationPrompts || [],
194
+ });
195
+ }
190
196
  }
191
197
  }
192
198
  }
@@ -211,12 +217,16 @@ export function calculateStats(questions, questionBank) {
211
217
 
212
218
  // Calculate full stats for skills and behaviours
213
219
  const skillStats = {};
214
- for (const [skillId, levels] of Object.entries(
220
+ for (const [skillId, roleTypes] of Object.entries(
215
221
  questionBank.skillLevels || {},
216
222
  )) {
217
223
  skillStats[skillId] = {};
218
224
  for (const level of SKILL_LEVELS) {
219
- skillStats[skillId][level] = (levels[level] || []).length;
225
+ let count = 0;
226
+ for (const roleType of ROLE_TYPES) {
227
+ count += (roleTypes[roleType]?.[level] || []).length;
228
+ }
229
+ skillStats[skillId][level] = count;
220
230
  }
221
231
  skillStats[skillId].total = Object.values(skillStats[skillId]).reduce(
222
232
  (a, b) => a + b,
@@ -225,14 +235,16 @@ export function calculateStats(questions, questionBank) {
225
235
  }
226
236
 
227
237
  const behaviourStats = {};
228
- for (const [behaviourId, maturities] of Object.entries(
238
+ for (const [behaviourId, roleTypes] of Object.entries(
229
239
  questionBank.behaviourMaturities || {},
230
240
  )) {
231
241
  behaviourStats[behaviourId] = {};
232
242
  for (const maturity of BEHAVIOUR_MATURITIES) {
233
- behaviourStats[behaviourId][maturity] = (
234
- maturities[maturity] || []
235
- ).length;
243
+ let count = 0;
244
+ for (const roleType of ROLE_TYPES) {
245
+ count += (roleTypes[roleType]?.[maturity] || []).length;
246
+ }
247
+ behaviourStats[behaviourId][maturity] = count;
236
248
  }
237
249
  behaviourStats[behaviourId].total = Object.values(
238
250
  behaviourStats[behaviourId],
@@ -34,33 +34,36 @@ export function stageToDOM(stage, { stages = [], showBackLink = true } = {}) {
34
34
  p({ className: "page-description" }, view.description),
35
35
  ),
36
36
 
37
- // Entry/Exit Criteria
38
- view.entryCriteria.length > 0 || view.exitCriteria.length > 0
37
+ // Read/Confirm Checklists
38
+ view.readChecklist.length > 0 || view.confirmChecklist.length > 0
39
39
  ? div(
40
40
  { className: "section section-detail" },
41
41
  div(
42
42
  { className: "content-columns" },
43
- // Entry criteria column
44
- view.entryCriteria.length > 0
43
+ // Read checklist column
44
+ view.readChecklist.length > 0
45
45
  ? div(
46
46
  { className: "column" },
47
- h2({ className: "section-title" }, "Entry Criteria"),
47
+ h2({ className: "section-title" }, "Read-Then-Do Checklist"),
48
48
  ul(
49
49
  { className: "criteria-list" },
50
- ...view.entryCriteria.map((item) =>
50
+ ...view.readChecklist.map((item) =>
51
51
  li({ className: "criteria-item" }, item),
52
52
  ),
53
53
  ),
54
54
  )
55
55
  : null,
56
- // Exit criteria column
57
- view.exitCriteria.length > 0
56
+ // Confirm checklist column
57
+ view.confirmChecklist.length > 0
58
58
  ? div(
59
59
  { className: "column" },
60
- h2({ className: "section-title" }, "Exit Criteria"),
60
+ h2(
61
+ { className: "section-title" },
62
+ "Do-Then-Confirm Checklist",
63
+ ),
61
64
  ul(
62
65
  { className: "criteria-list" },
63
- ...view.exitCriteria.map((item) =>
66
+ ...view.confirmChecklist.map((item) =>
64
67
  li({ className: "criteria-item" }, item),
65
68
  ),
66
69
  ),
@@ -60,11 +60,11 @@ export function stageToMicrodata(stage) {
60
60
 
61
61
  const sections = [];
62
62
 
63
- // Entry criteria
64
- if (view.entryCriteria.length > 0) {
65
- const criteriaItems = view.entryCriteria.map((c) => escapeHtml(c));
63
+ // Read checklist
64
+ if (view.readChecklist.length > 0) {
65
+ const readItems = view.readChecklist.map((c) => escapeHtml(c));
66
66
  sections.push(
67
- section("Entry Criteria", ul(criteriaItems, "entryCriteria"), 2),
67
+ section("Read-Then-Do Checklist", ul(readItems, "readChecklist"), 2),
68
68
  );
69
69
  }
70
70
 
@@ -76,10 +76,16 @@ export function stageToMicrodata(stage) {
76
76
  );
77
77
  }
78
78
 
79
- // Exit criteria
80
- if (view.exitCriteria.length > 0) {
81
- const exitItems = view.exitCriteria.map((c) => escapeHtml(c));
82
- sections.push(section("Exit Criteria", ul(exitItems, "exitCriteria"), 2));
79
+ // Confirm checklist
80
+ if (view.confirmChecklist.length > 0) {
81
+ const confirmItems = view.confirmChecklist.map((c) => escapeHtml(c));
82
+ sections.push(
83
+ section(
84
+ "Do-Then-Confirm Checklist",
85
+ ul(confirmItems, "confirmChecklist"),
86
+ 2,
87
+ ),
88
+ );
83
89
  }
84
90
 
85
91
  // Handoffs - using Handoff itemtype
@@ -46,8 +46,8 @@ export function prepareStagesList(stages, descriptionLimit = 150) {
46
46
  * @property {string} name
47
47
  * @property {string} description
48
48
  * @property {string[]} constraints
49
- * @property {string[]} entryCriteria
50
- * @property {string[]} exitCriteria
49
+ * @property {string[]} readChecklist
50
+ * @property {string[]} confirmChecklist
51
51
  * @property {Array<{target: string, label: string, prompt: string}>} handoffs
52
52
  */
53
53
 
@@ -62,8 +62,8 @@ export function prepareStageDetail(stage) {
62
62
  name: stage.name,
63
63
  description: stage.description,
64
64
  constraints: stage.constraints || [],
65
- entryCriteria: stage.entryCriteria || [],
66
- exitCriteria: stage.exitCriteria || [],
65
+ readChecklist: stage.readChecklist || [],
66
+ confirmChecklist: stage.confirmChecklist || [],
67
67
  handoffs: (stage.handoffs || []).map((h) => ({
68
68
  target: h.targetStage,
69
69
  label: h.label,