@forwardimpact/map 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.
- package/README.md +1 -1
- package/bin/fit-map.js +12 -12
- package/package.json +9 -6
- package/schema/json/discipline.schema.json +6 -6
- package/schema/rdf/discipline.ttl +19 -19
- package/src/index-generator.js +67 -38
- package/src/index.js +10 -25
- package/src/loader.js +407 -562
- package/src/schema-validation.js +327 -307
- package/examples/behaviours/_index.yaml +0 -8
- package/examples/behaviours/outcome_ownership.yaml +0 -43
- package/examples/behaviours/polymathic_knowledge.yaml +0 -41
- package/examples/behaviours/precise_communication.yaml +0 -39
- package/examples/behaviours/relentless_curiosity.yaml +0 -37
- package/examples/behaviours/systems_thinking.yaml +0 -40
- package/examples/capabilities/_index.yaml +0 -8
- package/examples/capabilities/business.yaml +0 -205
- package/examples/capabilities/delivery.yaml +0 -1001
- package/examples/capabilities/people.yaml +0 -68
- package/examples/capabilities/reliability.yaml +0 -349
- package/examples/capabilities/scale.yaml +0 -1672
- package/examples/copilot-setup-steps.yaml +0 -25
- package/examples/devcontainer.yaml +0 -21
- package/examples/disciplines/_index.yaml +0 -6
- package/examples/disciplines/data_engineering.yaml +0 -68
- package/examples/disciplines/engineering_management.yaml +0 -61
- package/examples/disciplines/software_engineering.yaml +0 -68
- package/examples/drivers.yaml +0 -202
- package/examples/framework.yaml +0 -73
- package/examples/levels.yaml +0 -115
- package/examples/questions/behaviours/outcome_ownership.yaml +0 -228
- package/examples/questions/behaviours/polymathic_knowledge.yaml +0 -275
- package/examples/questions/behaviours/precise_communication.yaml +0 -248
- package/examples/questions/behaviours/relentless_curiosity.yaml +0 -248
- package/examples/questions/behaviours/systems_thinking.yaml +0 -238
- package/examples/questions/capabilities/business.yaml +0 -107
- package/examples/questions/capabilities/delivery.yaml +0 -101
- package/examples/questions/capabilities/people.yaml +0 -106
- package/examples/questions/capabilities/reliability.yaml +0 -105
- package/examples/questions/capabilities/scale.yaml +0 -104
- package/examples/questions/skills/architecture_design.yaml +0 -115
- package/examples/questions/skills/cloud_platforms.yaml +0 -105
- package/examples/questions/skills/code_quality.yaml +0 -162
- package/examples/questions/skills/data_modeling.yaml +0 -107
- package/examples/questions/skills/devops.yaml +0 -111
- package/examples/questions/skills/full_stack_development.yaml +0 -118
- package/examples/questions/skills/sre_practices.yaml +0 -113
- package/examples/questions/skills/stakeholder_management.yaml +0 -116
- package/examples/questions/skills/team_collaboration.yaml +0 -106
- package/examples/questions/skills/technical_writing.yaml +0 -110
- package/examples/self-assessments.yaml +0 -64
- package/examples/stages.yaml +0 -191
- package/examples/tracks/_index.yaml +0 -5
- package/examples/tracks/platform.yaml +0 -47
- package/examples/tracks/sre.yaml +0 -46
- package/examples/vscode-settings.yaml +0 -21
package/README.md
CHANGED
package/bin/fit-map.js
CHANGED
|
@@ -72,11 +72,11 @@ async function findDataDir(providedPath) {
|
|
|
72
72
|
throw new Error(`Data directory not found: ${providedPath}`);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// Check common locations
|
|
76
75
|
const candidates = [
|
|
76
|
+
join(process.cwd(), "data/pathway"),
|
|
77
|
+
join(process.cwd(), "examples/pathway"),
|
|
77
78
|
join(process.cwd(), "data"),
|
|
78
79
|
join(process.cwd(), "examples"),
|
|
79
|
-
join(__dirname, "../examples"),
|
|
80
80
|
];
|
|
81
81
|
|
|
82
82
|
for (const candidate of candidates) {
|
|
@@ -124,17 +124,17 @@ function formatValidationResults(result) {
|
|
|
124
124
|
async function runValidate(dataDir) {
|
|
125
125
|
console.log(`🔍 Validating data in: ${dataDir}\n`);
|
|
126
126
|
|
|
127
|
-
const {
|
|
127
|
+
const { createDataLoader, createSchemaValidator } =
|
|
128
|
+
await import("../src/index.js");
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
const
|
|
130
|
+
const loader = createDataLoader();
|
|
131
|
+
const validator = createSchemaValidator();
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
const result = await
|
|
133
|
+
const data = await loader.loadAllData(dataDir);
|
|
134
|
+
const result = await validator.runFullValidation(dataDir, data);
|
|
134
135
|
|
|
135
136
|
console.log(formatValidationResults(result));
|
|
136
137
|
|
|
137
|
-
// Print summary
|
|
138
138
|
console.log("\n📊 Data Summary:");
|
|
139
139
|
console.log(` Skills: ${data.skills?.length || 0}`);
|
|
140
140
|
console.log(` Behaviours: ${data.behaviours?.length || 0}`);
|
|
@@ -152,9 +152,10 @@ async function runValidate(dataDir) {
|
|
|
152
152
|
async function runGenerateIndex(dataDir) {
|
|
153
153
|
console.log(`📁 Generating index files in: ${dataDir}\n`);
|
|
154
154
|
|
|
155
|
-
const {
|
|
155
|
+
const { createIndexGenerator } = await import("../src/index.js");
|
|
156
156
|
|
|
157
|
-
const
|
|
157
|
+
const generator = createIndexGenerator();
|
|
158
|
+
const results = await generator.generateAllIndexes(dataDir);
|
|
158
159
|
|
|
159
160
|
for (const [dir, files] of Object.entries(results)) {
|
|
160
161
|
if (files.error) {
|
|
@@ -179,7 +180,6 @@ async function runValidateShacl() {
|
|
|
179
180
|
const { default: N3 } = await import("n3");
|
|
180
181
|
const { readFile, readdir } = await import("fs/promises");
|
|
181
182
|
|
|
182
|
-
// Find all .ttl files in the RDF directory
|
|
183
183
|
const files = await readdir(rdfDir);
|
|
184
184
|
const ttlFiles = files.filter((f) => f.endsWith(".ttl")).sort();
|
|
185
185
|
|
|
@@ -229,7 +229,7 @@ async function runPeopleImport(filePath, dataDir) {
|
|
|
229
229
|
console.log(`👤 Importing people from: ${filePath}\n`);
|
|
230
230
|
|
|
231
231
|
const { loadPeopleFile, validatePeople } =
|
|
232
|
-
await import("../activity/
|
|
232
|
+
await import("../activity/transform/people.js");
|
|
233
233
|
|
|
234
234
|
const people = await loadPeopleFile(filePath);
|
|
235
235
|
console.log(` Loaded ${people.length} people from file`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/map",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Public data model for career frameworks, consumed by AI agents and engineers",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"bin/",
|
|
23
|
-
"examples/",
|
|
24
23
|
"src/",
|
|
25
24
|
"schema/"
|
|
26
25
|
],
|
|
@@ -31,16 +30,20 @@
|
|
|
31
30
|
"./schema-validation": "./src/schema-validation.js",
|
|
32
31
|
"./index-generator": "./src/index-generator.js",
|
|
33
32
|
"./levels": "./src/levels.js",
|
|
34
|
-
"./examples/*": "./examples/*",
|
|
35
33
|
"./schema/json/*": "./schema/json/*",
|
|
36
34
|
"./schema/rdf/*": "./schema/rdf/*",
|
|
37
35
|
"./activity/queries/org": "./activity/queries/org.js",
|
|
38
36
|
"./activity/queries/snapshots": "./activity/queries/snapshots.js",
|
|
39
37
|
"./activity/queries/evidence": "./activity/queries/evidence.js",
|
|
40
38
|
"./activity/queries/artifacts": "./activity/queries/artifacts.js",
|
|
41
|
-
"./activity/
|
|
42
|
-
"./activity/
|
|
43
|
-
"./activity/
|
|
39
|
+
"./activity/storage": "./activity/storage.js",
|
|
40
|
+
"./activity/extract/github": "./activity/extract/github.js",
|
|
41
|
+
"./activity/extract/getdx": "./activity/extract/getdx.js",
|
|
42
|
+
"./activity/extract/people": "./activity/extract/people.js",
|
|
43
|
+
"./activity/transform/github": "./activity/transform/github.js",
|
|
44
|
+
"./activity/transform/getdx": "./activity/transform/getdx.js",
|
|
45
|
+
"./activity/transform/people": "./activity/transform/people.js",
|
|
46
|
+
"./activity/transform": "./activity/transform/index.js"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {
|
|
46
49
|
"ajv": "^8.17.1",
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"type": "boolean",
|
|
23
23
|
"description": "If true, this is a management discipline (default false)"
|
|
24
24
|
},
|
|
25
|
+
"hidden": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"description": "If true, this discipline is hidden from the web UI but remains available via the CLI"
|
|
28
|
+
},
|
|
25
29
|
"validTracks": {
|
|
26
30
|
"type": "array",
|
|
27
31
|
"description": "REQUIRED. Explicit list of valid tracks. Use null to allow trackless (generalist). Empty array = no valid combinations.",
|
|
@@ -88,13 +92,9 @@
|
|
|
88
92
|
"disciplineHumanSection": {
|
|
89
93
|
"type": "object",
|
|
90
94
|
"properties": {
|
|
91
|
-
"
|
|
92
|
-
"type": "string",
|
|
93
|
-
"description": "Role summary for professional/IC track. May use {roleTitle} placeholder."
|
|
94
|
-
},
|
|
95
|
-
"managementRoleSummary": {
|
|
95
|
+
"roleSummary": {
|
|
96
96
|
"type": "string",
|
|
97
|
-
"description": "Role summary for
|
|
97
|
+
"description": "Role summary for this discipline. May use {roleTitle} or {specialization} placeholder."
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
100
|
"additionalProperties": false
|
|
@@ -58,6 +58,12 @@ fit:isManagement a rdf:Property ;
|
|
|
58
58
|
rdfs:domain fit:Discipline ;
|
|
59
59
|
rdfs:range xsd:boolean .
|
|
60
60
|
|
|
61
|
+
fit:hidden a rdf:Property ;
|
|
62
|
+
rdfs:label "hidden"@en ;
|
|
63
|
+
rdfs:comment "If true, this discipline is hidden from the web UI but remains available via the CLI"@en ;
|
|
64
|
+
rdfs:domain fit:Discipline ;
|
|
65
|
+
rdfs:range xsd:boolean .
|
|
66
|
+
|
|
61
67
|
fit:validTracks a rdf:Property ;
|
|
62
68
|
rdfs:label "validTracks"@en ;
|
|
63
69
|
rdfs:comment "REQUIRED. Explicit list of valid tracks. Empty array = trackless only."@en ;
|
|
@@ -86,15 +92,9 @@ fit:behaviourModifiers a rdf:Property ;
|
|
|
86
92
|
rdfs:label "behaviourModifiers"@en ;
|
|
87
93
|
rdfs:comment "Modifiers to behaviour expectations"@en .
|
|
88
94
|
|
|
89
|
-
fit:
|
|
90
|
-
rdfs:label "
|
|
91
|
-
rdfs:comment "Role summary for
|
|
92
|
-
rdfs:domain fit:DisciplineHumanSection ;
|
|
93
|
-
rdfs:range xsd:string .
|
|
94
|
-
|
|
95
|
-
fit:managementRoleSummary a rdf:Property ;
|
|
96
|
-
rdfs:label "managementRoleSummary"@en ;
|
|
97
|
-
rdfs:comment "Role summary for management track. May use {specialization} placeholder."@en ;
|
|
95
|
+
fit:roleSummary a rdf:Property ;
|
|
96
|
+
rdfs:label "roleSummary"@en ;
|
|
97
|
+
rdfs:comment "Role summary for this discipline. May use {roleTitle} or {specialization} placeholder."@en ;
|
|
98
98
|
rdfs:domain fit:DisciplineHumanSection ;
|
|
99
99
|
rdfs:range xsd:string .
|
|
100
100
|
|
|
@@ -169,6 +169,13 @@ fit:DisciplineShape a sh:NodeShape ;
|
|
|
169
169
|
sh:name "isManagement" ;
|
|
170
170
|
sh:description "If true, this is a management discipline (default false)" ;
|
|
171
171
|
] ;
|
|
172
|
+
sh:property [
|
|
173
|
+
sh:path fit:hidden ;
|
|
174
|
+
sh:datatype xsd:boolean ;
|
|
175
|
+
sh:maxCount 1 ;
|
|
176
|
+
sh:name "hidden" ;
|
|
177
|
+
sh:description "If true, this discipline is hidden from the web UI but remains available via the CLI" ;
|
|
178
|
+
] ;
|
|
172
179
|
sh:property [
|
|
173
180
|
sh:path fit:validTracks ;
|
|
174
181
|
sh:minCount 1 ;
|
|
@@ -244,18 +251,11 @@ fit:DisciplineShape a sh:NodeShape ;
|
|
|
244
251
|
fit:DisciplineHumanSectionShape a sh:NodeShape ;
|
|
245
252
|
sh:targetClass fit:DisciplineHumanSection ;
|
|
246
253
|
sh:property [
|
|
247
|
-
sh:path fit:
|
|
248
|
-
sh:datatype xsd:string ;
|
|
249
|
-
sh:maxCount 1 ;
|
|
250
|
-
sh:name "professionalRoleSummary" ;
|
|
251
|
-
sh:description "Role summary for professional/IC track" ;
|
|
252
|
-
] ;
|
|
253
|
-
sh:property [
|
|
254
|
-
sh:path fit:managementRoleSummary ;
|
|
254
|
+
sh:path fit:roleSummary ;
|
|
255
255
|
sh:datatype xsd:string ;
|
|
256
256
|
sh:maxCount 1 ;
|
|
257
|
-
sh:name "
|
|
258
|
-
sh:description "Role summary for
|
|
257
|
+
sh:name "roleSummary" ;
|
|
258
|
+
sh:description "Role summary for this discipline" ;
|
|
259
259
|
] .
|
|
260
260
|
|
|
261
261
|
# -----------------------------------------------------------------------------
|
package/src/index-generator.js
CHANGED
|
@@ -10,56 +10,85 @@ import { join, basename } from "path";
|
|
|
10
10
|
import { stringify as stringifyYaml } from "yaml";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param {string} dir - Directory path
|
|
15
|
-
* @returns {Promise<string[]>} List of file IDs included
|
|
13
|
+
* Index generator class with injectable filesystem and serializer dependencies.
|
|
16
14
|
*/
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
export class IndexGenerator {
|
|
16
|
+
#fs;
|
|
17
|
+
#yaml;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {{ readdir: Function, writeFile: Function }} fs
|
|
21
|
+
* @param {{ stringify: Function }} yamlSerializer
|
|
22
|
+
*/
|
|
23
|
+
constructor(fs, yamlSerializer) {
|
|
24
|
+
if (!fs) throw new Error("fs is required");
|
|
25
|
+
if (!yamlSerializer) throw new Error("yamlSerializer is required");
|
|
26
|
+
this.#fs = fs;
|
|
27
|
+
this.#yaml = yamlSerializer;
|
|
28
|
+
}
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Generate _index.yaml for a directory
|
|
32
|
+
* @param {string} dir - Directory path
|
|
33
|
+
* @returns {Promise<string[]>} List of file IDs included
|
|
34
|
+
*/
|
|
35
|
+
async generateDirIndex(dir) {
|
|
36
|
+
const files = await this.#fs.readdir(dir);
|
|
37
|
+
const yamlFiles = files.filter(
|
|
38
|
+
(f) => f.endsWith(".yaml") && !f.startsWith("_"),
|
|
39
|
+
);
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
// Auto-generated index for browser loading
|
|
28
|
-
// Do not edit manually - regenerate with: npx pathway --generate-index
|
|
29
|
-
files: fileIds,
|
|
30
|
-
},
|
|
31
|
-
{ lineWidth: 0 },
|
|
32
|
-
);
|
|
41
|
+
const fileIds = yamlFiles.map((f) => basename(f, ".yaml")).sort();
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const content = this.#yaml.stringify(
|
|
44
|
+
{
|
|
45
|
+
// Auto-generated index for browser loading
|
|
46
|
+
// Do not edit manually - regenerate with: npx pathway --generate-index
|
|
47
|
+
files: fileIds,
|
|
48
|
+
},
|
|
49
|
+
{ lineWidth: 0 },
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const output = `# Auto-generated index for browser loading
|
|
36
53
|
# Do not edit manually - regenerate with: npx pathway --generate-index
|
|
37
54
|
${content}`;
|
|
38
55
|
|
|
39
|
-
|
|
56
|
+
await this.#fs.writeFile(join(dir, "_index.yaml"), output, "utf-8");
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
}
|
|
58
|
+
return fileIds;
|
|
59
|
+
}
|
|
43
60
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Generate all index files for the data directory
|
|
63
|
+
* @param {string} dataDir - Path to the data directory
|
|
64
|
+
* @returns {Promise<Object>} Summary of generated indexes
|
|
65
|
+
*/
|
|
66
|
+
async generateAllIndexes(dataDir) {
|
|
67
|
+
const directories = ["behaviours", "disciplines", "tracks", "capabilities"];
|
|
51
68
|
|
|
52
|
-
|
|
69
|
+
const results = {};
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
for (const dir of directories) {
|
|
72
|
+
const fullPath = join(dataDir, dir);
|
|
73
|
+
try {
|
|
74
|
+
const files = await this.generateDirIndex(fullPath);
|
|
75
|
+
results[dir] = files;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
results[dir] = { error: err.message };
|
|
78
|
+
}
|
|
61
79
|
}
|
|
80
|
+
|
|
81
|
+
return results;
|
|
62
82
|
}
|
|
83
|
+
}
|
|
63
84
|
|
|
64
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Create an IndexGenerator with real filesystem and serializer dependencies
|
|
87
|
+
* @returns {IndexGenerator}
|
|
88
|
+
*/
|
|
89
|
+
export function createIndexGenerator() {
|
|
90
|
+
return new IndexGenerator(
|
|
91
|
+
{ readdir, writeFile },
|
|
92
|
+
{ stringify: stringifyYaml },
|
|
93
|
+
);
|
|
65
94
|
}
|
package/src/index.js
CHANGED
|
@@ -4,22 +4,17 @@
|
|
|
4
4
|
* Data model definitions, validation, and loading for Engineering Pathway.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
loadExampleData,
|
|
17
|
-
loadAndValidate,
|
|
18
|
-
loadAgentData,
|
|
19
|
-
loadSkillsWithAgentData,
|
|
20
|
-
} from "./loader.js";
|
|
7
|
+
// Classes
|
|
8
|
+
export { DataLoader } from "./loader.js";
|
|
9
|
+
export { SchemaValidator } from "./schema-validation.js";
|
|
10
|
+
export { IndexGenerator } from "./index-generator.js";
|
|
11
|
+
|
|
12
|
+
// Factory functions
|
|
13
|
+
export { createDataLoader } from "./loader.js";
|
|
14
|
+
export { createSchemaValidator } from "./schema-validation.js";
|
|
15
|
+
export { createIndexGenerator } from "./index-generator.js";
|
|
21
16
|
|
|
22
|
-
//
|
|
17
|
+
// Pure validation functions (unchanged)
|
|
23
18
|
export {
|
|
24
19
|
validateAllData,
|
|
25
20
|
validateQuestionBank,
|
|
@@ -27,16 +22,6 @@ export {
|
|
|
27
22
|
validateAgentData,
|
|
28
23
|
} from "./validation.js";
|
|
29
24
|
|
|
30
|
-
// Schema-based validation
|
|
31
|
-
export {
|
|
32
|
-
validateDataDirectory,
|
|
33
|
-
validateReferentialIntegrity,
|
|
34
|
-
runSchemaValidation,
|
|
35
|
-
} from "./schema-validation.js";
|
|
36
|
-
|
|
37
|
-
// Index generation
|
|
38
|
-
export { generateAllIndexes, generateDirIndex } from "./index-generator.js";
|
|
39
|
-
|
|
40
25
|
// Type constants and helpers
|
|
41
26
|
export * from "./levels.js";
|
|
42
27
|
|