@forwardimpact/schema 0.4.0 → 0.7.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 (48) hide show
  1. package/bin/fit-schema.js +2 -2
  2. package/examples/capabilities/business.yaml +27 -11
  3. package/examples/capabilities/delivery.yaml +65 -27
  4. package/examples/capabilities/people.yaml +1 -1
  5. package/examples/capabilities/reliability.yaml +85 -31
  6. package/examples/capabilities/scale.yaml +83 -31
  7. package/examples/framework.yaml +5 -1
  8. package/examples/questions/behaviours/outcome_ownership.yaml +226 -49
  9. package/examples/questions/behaviours/polymathic_knowledge.yaml +273 -45
  10. package/examples/questions/behaviours/precise_communication.yaml +246 -52
  11. package/examples/questions/behaviours/relentless_curiosity.yaml +246 -48
  12. package/examples/questions/behaviours/systems_thinking.yaml +236 -50
  13. package/examples/questions/capabilities/business.yaml +107 -0
  14. package/examples/questions/capabilities/delivery.yaml +104 -0
  15. package/examples/questions/capabilities/people.yaml +104 -0
  16. package/examples/questions/capabilities/reliability.yaml +103 -0
  17. package/examples/questions/capabilities/scale.yaml +103 -0
  18. package/examples/questions/skills/architecture_design.yaml +102 -51
  19. package/examples/questions/skills/cloud_platforms.yaml +90 -44
  20. package/examples/questions/skills/code_quality.yaml +86 -45
  21. package/examples/questions/skills/data_modeling.yaml +93 -43
  22. package/examples/questions/skills/devops.yaml +91 -44
  23. package/examples/questions/skills/full_stack_development.yaml +93 -45
  24. package/examples/questions/skills/sre_practices.yaml +92 -41
  25. package/examples/questions/skills/stakeholder_management.yaml +97 -46
  26. package/examples/questions/skills/team_collaboration.yaml +87 -40
  27. package/examples/questions/skills/technical_writing.yaml +89 -40
  28. package/examples/stages.yaml +52 -13
  29. package/package.json +9 -9
  30. package/schema/json/behaviour-questions.schema.json +53 -26
  31. package/schema/json/capability-questions.schema.json +95 -0
  32. package/schema/json/capability.schema.json +8 -7
  33. package/schema/json/framework.schema.json +13 -0
  34. package/schema/json/skill-questions.schema.json +34 -19
  35. package/schema/json/stages.schema.json +6 -6
  36. package/schema/rdf/behaviour-questions.ttl +39 -7
  37. package/schema/rdf/capability.ttl +15 -15
  38. package/schema/rdf/defs.ttl +3 -3
  39. package/schema/rdf/framework.ttl +38 -0
  40. package/schema/rdf/skill-questions.ttl +28 -1
  41. package/schema/rdf/stages.ttl +14 -14
  42. package/{lib → src}/levels.js +53 -101
  43. package/{lib → src}/loader.js +9 -5
  44. package/{lib → src}/modifiers.js +3 -3
  45. package/{lib → src}/validation.js +105 -79
  46. /package/{lib → src}/index-generator.js +0 -0
  47. /package/{lib → src}/index.js +0 -0
  48. /package/{lib → src}/schema-validation.js +0 -0
@@ -6,7 +6,6 @@
6
6
 
7
7
  import {
8
8
  Capability,
9
- Stage,
10
9
  getSkillLevelIndex,
11
10
  getBehaviourMaturityIndex,
12
11
  } from "./levels.js";
@@ -150,14 +149,13 @@ function validateSkill(skill, index, requiredStageIds = []) {
150
149
  ),
151
150
  );
152
151
  } else {
153
- // Validate each stage
154
- const validStageIds = Object.values(Stage);
152
+ // Validate each stage against loaded stage data
155
153
  for (const [stageId, stageData] of Object.entries(skill.agent.stages)) {
156
- if (!validStageIds.includes(stageId)) {
154
+ if (requiredStageIds.length > 0 && !requiredStageIds.includes(stageId)) {
157
155
  errors.push(
158
156
  createError(
159
157
  "INVALID_VALUE",
160
- `Invalid stage ID: ${stageId}. Must be one of: ${validStageIds.join(", ")}`,
158
+ `Invalid stage ID: ${stageId}. Must be one of: ${requiredStageIds.join(", ")}`,
161
159
  `${agentPath}.stages.${stageId}`,
162
160
  stageId,
163
161
  ),
@@ -184,41 +182,41 @@ function validateSkill(skill, index, requiredStageIds = []) {
184
182
  ),
185
183
  );
186
184
  }
187
- // activities is required and must be an array
188
- if (!stageData.activities) {
185
+ // readChecklist is required and must be an array
186
+ if (!stageData.readChecklist) {
189
187
  errors.push(
190
188
  createError(
191
189
  "MISSING_REQUIRED",
192
- `Stage ${stageId} missing activities`,
193
- `${stagePath}.activities`,
190
+ `Stage ${stageId} missing readChecklist`,
191
+ `${stagePath}.readChecklist`,
194
192
  ),
195
193
  );
196
- } else if (!Array.isArray(stageData.activities)) {
194
+ } else if (!Array.isArray(stageData.readChecklist)) {
197
195
  errors.push(
198
196
  createError(
199
197
  "INVALID_VALUE",
200
- `Stage ${stageId} activities must be an array`,
201
- `${stagePath}.activities`,
202
- stageData.activities,
198
+ `Stage ${stageId} readChecklist must be an array`,
199
+ `${stagePath}.readChecklist`,
200
+ stageData.readChecklist,
203
201
  ),
204
202
  );
205
203
  }
206
- // ready is required and must be an array (these become checklist items)
207
- if (!stageData.ready) {
204
+ // confirmChecklist is required and must be an array (these become checklist items)
205
+ if (!stageData.confirmChecklist) {
208
206
  errors.push(
209
207
  createError(
210
208
  "MISSING_REQUIRED",
211
- `Stage ${stageId} missing ready criteria`,
212
- `${stagePath}.ready`,
209
+ `Stage ${stageId} missing confirmChecklist`,
210
+ `${stagePath}.confirmChecklist`,
213
211
  ),
214
212
  );
215
- } else if (!Array.isArray(stageData.ready)) {
213
+ } else if (!Array.isArray(stageData.confirmChecklist)) {
216
214
  errors.push(
217
215
  createError(
218
216
  "INVALID_VALUE",
219
- `Stage ${stageId} ready must be an array`,
220
- `${stagePath}.ready`,
221
- stageData.ready,
217
+ `Stage ${stageId} confirmChecklist must be an array`,
218
+ `${stagePath}.confirmChecklist`,
219
+ stageData.confirmChecklist,
222
220
  ),
223
221
  );
224
222
  }
@@ -284,7 +282,7 @@ function validateSkill(skill, index, requiredStageIds = []) {
284
282
  errors.push(
285
283
  createError(
286
284
  "INVALID_FIELD",
287
- "Skill agent 'verificationCriteria' field is not supported. Use stages.{stage}.ready instead.",
285
+ "Skill agent 'verificationCriteria' field is not supported. Use stages.{stage}.confirmChecklist instead.",
288
286
  `${agentPath}.verificationCriteria`,
289
287
  ),
290
288
  );
@@ -1249,15 +1247,6 @@ function validateStage(stage, index) {
1249
1247
 
1250
1248
  if (!stage.id) {
1251
1249
  errors.push(createError("MISSING_REQUIRED", "Stage missing id", path));
1252
- } else if (!Object.values(Stage).includes(stage.id)) {
1253
- errors.push(
1254
- createError(
1255
- "INVALID_VALUE",
1256
- `Invalid stage id: ${stage.id}`,
1257
- `${path}.id`,
1258
- stage.id,
1259
- ),
1260
- );
1261
1250
  }
1262
1251
 
1263
1252
  if (!stage.name) {
@@ -1774,6 +1763,7 @@ export function validateQuestionBank(questionBank, skills, behaviours) {
1774
1763
  const warnings = [];
1775
1764
  const skillIds = new Set(skills.map((s) => s.id));
1776
1765
  const behaviourIds = new Set(behaviours.map((b) => b.id));
1766
+ const validRoleTypes = ["professionalQuestions", "managementQuestions"];
1777
1767
 
1778
1768
  if (!questionBank) {
1779
1769
  return createValidationResult(false, [
@@ -1784,7 +1774,7 @@ export function validateQuestionBank(questionBank, skills, behaviours) {
1784
1774
  // Validate skill questions
1785
1775
  if (questionBank.skillLevels) {
1786
1776
  Object.entries(questionBank.skillLevels).forEach(
1787
- ([skillId, levelQuestions]) => {
1777
+ ([skillId, roleTypeQuestions]) => {
1788
1778
  if (!skillIds.has(skillId)) {
1789
1779
  errors.push(
1790
1780
  createError(
@@ -1795,27 +1785,46 @@ export function validateQuestionBank(questionBank, skills, behaviours) {
1795
1785
  ),
1796
1786
  );
1797
1787
  }
1798
- Object.entries(levelQuestions || {}).forEach(([level, questions]) => {
1799
- if (getSkillLevelIndex(level) === -1) {
1800
- errors.push(
1801
- createError(
1802
- "INVALID_VALUE",
1803
- `Question bank has invalid skill level: ${level}`,
1804
- `questionBank.skillLevels.${skillId}.${level}`,
1805
- level,
1806
- ),
1807
- );
1808
- }
1809
- if (!Array.isArray(questions) || questions.length === 0) {
1810
- warnings.push(
1811
- createWarning(
1812
- "EMPTY_QUESTIONS",
1813
- `No questions for skill ${skillId} at level ${level}`,
1814
- `questionBank.skillLevels.${skillId}.${level}`,
1815
- ),
1788
+ // Validate each role type (professional/management)
1789
+ Object.entries(roleTypeQuestions || {}).forEach(
1790
+ ([roleType, levelQuestions]) => {
1791
+ if (!validRoleTypes.includes(roleType)) {
1792
+ errors.push(
1793
+ createError(
1794
+ "INVALID_VALUE",
1795
+ `Question bank has invalid role type: ${roleType}`,
1796
+ `questionBank.skillLevels.${skillId}.${roleType}`,
1797
+ roleType,
1798
+ ),
1799
+ );
1800
+ return;
1801
+ }
1802
+ // Validate each level within the role type
1803
+ Object.entries(levelQuestions || {}).forEach(
1804
+ ([level, questions]) => {
1805
+ if (getSkillLevelIndex(level) === -1) {
1806
+ errors.push(
1807
+ createError(
1808
+ "INVALID_VALUE",
1809
+ `Question bank has invalid skill level: ${level}`,
1810
+ `questionBank.skillLevels.${skillId}.${roleType}.${level}`,
1811
+ level,
1812
+ ),
1813
+ );
1814
+ }
1815
+ if (!Array.isArray(questions) || questions.length === 0) {
1816
+ warnings.push(
1817
+ createWarning(
1818
+ "EMPTY_QUESTIONS",
1819
+ `No questions for skill ${skillId} (${roleType}) at level ${level}`,
1820
+ `questionBank.skillLevels.${skillId}.${roleType}.${level}`,
1821
+ ),
1822
+ );
1823
+ }
1824
+ },
1816
1825
  );
1817
- }
1818
- });
1826
+ },
1827
+ );
1819
1828
  },
1820
1829
  );
1821
1830
  }
@@ -1823,7 +1832,7 @@ export function validateQuestionBank(questionBank, skills, behaviours) {
1823
1832
  // Validate behaviour questions
1824
1833
  if (questionBank.behaviourMaturities) {
1825
1834
  Object.entries(questionBank.behaviourMaturities).forEach(
1826
- ([behaviourId, maturityQuestions]) => {
1835
+ ([behaviourId, roleTypeQuestions]) => {
1827
1836
  if (!behaviourIds.has(behaviourId)) {
1828
1837
  errors.push(
1829
1838
  createError(
@@ -1834,27 +1843,44 @@ export function validateQuestionBank(questionBank, skills, behaviours) {
1834
1843
  ),
1835
1844
  );
1836
1845
  }
1837
- Object.entries(maturityQuestions || {}).forEach(
1838
- ([maturity, questions]) => {
1839
- if (getBehaviourMaturityIndex(maturity) === -1) {
1846
+ // Validate each role type (professional/management)
1847
+ Object.entries(roleTypeQuestions || {}).forEach(
1848
+ ([roleType, maturityQuestions]) => {
1849
+ if (!validRoleTypes.includes(roleType)) {
1840
1850
  errors.push(
1841
1851
  createError(
1842
1852
  "INVALID_VALUE",
1843
- `Question bank has invalid behaviour maturity: ${maturity}`,
1844
- `questionBank.behaviourMaturities.${behaviourId}.${maturity}`,
1845
- maturity,
1846
- ),
1847
- );
1848
- }
1849
- if (!Array.isArray(questions) || questions.length === 0) {
1850
- warnings.push(
1851
- createWarning(
1852
- "EMPTY_QUESTIONS",
1853
- `No questions for behaviour ${behaviourId} at maturity ${maturity}`,
1854
- `questionBank.behaviourMaturities.${behaviourId}.${maturity}`,
1853
+ `Question bank has invalid role type: ${roleType}`,
1854
+ `questionBank.behaviourMaturities.${behaviourId}.${roleType}`,
1855
+ roleType,
1855
1856
  ),
1856
1857
  );
1858
+ return;
1857
1859
  }
1860
+ // Validate each maturity level within the role type
1861
+ Object.entries(maturityQuestions || {}).forEach(
1862
+ ([maturity, questions]) => {
1863
+ if (getBehaviourMaturityIndex(maturity) === -1) {
1864
+ errors.push(
1865
+ createError(
1866
+ "INVALID_VALUE",
1867
+ `Question bank has invalid behaviour maturity: ${maturity}`,
1868
+ `questionBank.behaviourMaturities.${behaviourId}.${roleType}.${maturity}`,
1869
+ maturity,
1870
+ ),
1871
+ );
1872
+ }
1873
+ if (!Array.isArray(questions) || questions.length === 0) {
1874
+ warnings.push(
1875
+ createWarning(
1876
+ "EMPTY_QUESTIONS",
1877
+ `No questions for behaviour ${behaviourId} (${roleType}) at maturity ${maturity}`,
1878
+ `questionBank.behaviourMaturities.${behaviourId}.${roleType}.${maturity}`,
1879
+ ),
1880
+ );
1881
+ }
1882
+ },
1883
+ );
1858
1884
  },
1859
1885
  );
1860
1886
  },
@@ -1960,7 +1986,7 @@ export function validateAgentData({ humanData, agentData }) {
1960
1986
 
1961
1987
  // Validate skills with agent sections have complete stage coverage
1962
1988
  const skillsWithAgent = (humanData.skills || []).filter((s) => s.agent);
1963
- const requiredStages = ["plan", "code", "review"];
1989
+ const requiredStages = ["plan", "onboard", "code", "review"];
1964
1990
 
1965
1991
  for (const skill of skillsWithAgent) {
1966
1992
  const stages = skill.agent.stages || {};
@@ -1988,28 +2014,28 @@ export function validateAgentData({ humanData, agentData }) {
1988
2014
  );
1989
2015
  }
1990
2016
  if (
1991
- !stageData.activities ||
1992
- !Array.isArray(stageData.activities) ||
1993
- stageData.activities.length === 0
2017
+ !stageData.readChecklist ||
2018
+ !Array.isArray(stageData.readChecklist) ||
2019
+ stageData.readChecklist.length === 0
1994
2020
  ) {
1995
2021
  errors.push(
1996
2022
  createError(
1997
2023
  "MISSING_REQUIRED",
1998
- `Skill '${skill.id}' agent stage '${stageId}' missing or empty activities`,
1999
- `skills.${skill.id}.agent.stages.${stageId}.activities`,
2024
+ `Skill '${skill.id}' agent stage '${stageId}' missing or empty readChecklist`,
2025
+ `skills.${skill.id}.agent.stages.${stageId}.readChecklist`,
2000
2026
  ),
2001
2027
  );
2002
2028
  }
2003
2029
  if (
2004
- !stageData.ready ||
2005
- !Array.isArray(stageData.ready) ||
2006
- stageData.ready.length === 0
2030
+ !stageData.confirmChecklist ||
2031
+ !Array.isArray(stageData.confirmChecklist) ||
2032
+ stageData.confirmChecklist.length === 0
2007
2033
  ) {
2008
2034
  errors.push(
2009
2035
  createError(
2010
2036
  "MISSING_REQUIRED",
2011
- `Skill '${skill.id}' agent stage '${stageId}' missing or empty ready criteria`,
2012
- `skills.${skill.id}.agent.stages.${stageId}.ready`,
2037
+ `Skill '${skill.id}' agent stage '${stageId}' missing or empty confirmChecklist`,
2038
+ `skills.${skill.id}.agent.stages.${stageId}.confirmChecklist`,
2013
2039
  ),
2014
2040
  );
2015
2041
  }
File without changes
File without changes
File without changes