@forwardimpact/pathway 0.4.0 β†’ 0.5.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 (69) hide show
  1. package/app/commands/behaviour.js +1 -1
  2. package/app/commands/command-factory.js +2 -2
  3. package/app/commands/discipline.js +1 -1
  4. package/app/commands/driver.js +1 -1
  5. package/app/commands/grade.js +1 -1
  6. package/app/commands/serve.js +2 -2
  7. package/app/commands/site.js +22 -2
  8. package/app/commands/skill.js +1 -1
  9. package/app/commands/stage.js +1 -1
  10. package/app/commands/track.js +1 -1
  11. package/app/components/card.js +11 -1
  12. package/app/components/code-display.js +153 -0
  13. package/app/components/markdown-textarea.js +68 -47
  14. package/app/css/bundles/app.css +14 -0
  15. package/app/css/components/badges.css +15 -8
  16. package/app/css/components/forms.css +23 -13
  17. package/app/css/components/surfaces.css +49 -3
  18. package/app/css/components/typography.css +1 -2
  19. package/app/css/pages/agent-builder.css +11 -102
  20. package/app/css/pages/detail.css +11 -1
  21. package/app/css/tokens.css +3 -0
  22. package/app/formatters/agent/dom.js +26 -71
  23. package/app/formatters/agent/profile.js +11 -6
  24. package/app/formatters/grade/dom.js +6 -6
  25. package/app/formatters/job/dom.js +3 -3
  26. package/app/formatters/json-ld.js +1 -1
  27. package/app/formatters/skill/dom.js +68 -56
  28. package/app/formatters/skill/shared.js +3 -1
  29. package/app/formatters/stage/microdata.js +2 -2
  30. package/app/formatters/stage/shared.js +3 -3
  31. package/app/formatters/tool/shared.js +6 -0
  32. package/app/handout-main.js +12 -11
  33. package/app/index.html +7 -1
  34. package/app/lib/card-mappers.js +27 -0
  35. package/app/model/agent.js +21 -5
  36. package/app/model/checklist.js +2 -2
  37. package/app/model/derivation.js +2 -2
  38. package/app/model/levels.js +2 -2
  39. package/app/model/validation.js +3 -3
  40. package/app/pages/agent-builder.js +119 -75
  41. package/app/pages/landing.js +1 -1
  42. package/app/pages/stage.js +4 -4
  43. package/app/slide-main.js +1 -1
  44. package/app/slides/chapter.js +8 -8
  45. package/app/slides/index.js +1 -1
  46. package/app/slides/overview.js +8 -8
  47. package/app/slides/skill.js +1 -0
  48. package/examples/capabilities/business.yaml +1 -1
  49. package/examples/capabilities/delivery.yaml +3 -1
  50. package/examples/capabilities/people.yaml +1 -1
  51. package/examples/capabilities/reliability.yaml +3 -1
  52. package/examples/capabilities/scale.yaml +1 -1
  53. package/examples/framework.yaml +11 -11
  54. package/examples/stages.yaml +18 -10
  55. package/package.json +2 -1
  56. package/templates/agent.template.md +47 -17
  57. package/templates/job.template.md +8 -8
  58. package/templates/skill.template.md +12 -9
  59. package/examples/agents/.claude/skills/architecture-design/SKILL.md +0 -130
  60. package/examples/agents/.claude/skills/cloud-platforms/SKILL.md +0 -131
  61. package/examples/agents/.claude/skills/code-quality-review/SKILL.md +0 -108
  62. package/examples/agents/.claude/skills/devops-cicd/SKILL.md +0 -142
  63. package/examples/agents/.claude/skills/full-stack-development/SKILL.md +0 -134
  64. package/examples/agents/.claude/skills/sre-practices/SKILL.md +0 -163
  65. package/examples/agents/.claude/skills/technical-debt-management/SKILL.md +0 -164
  66. package/examples/agents/.github/agents/se-platform-code.agent.md +0 -132
  67. package/examples/agents/.github/agents/se-platform-plan.agent.md +0 -131
  68. package/examples/agents/.github/agents/se-platform-review.agent.md +0 -136
  69. package/examples/agents/.vscode/settings.json +0 -8
@@ -16,6 +16,8 @@ import {
16
16
  span,
17
17
  label,
18
18
  section,
19
+ select,
20
+ option,
19
21
  } from "../lib/render.js";
20
22
  import { getState } from "../lib/state.js";
21
23
  import { loadAgentDataBrowser } from "../lib/yaml-loader.js";
@@ -34,6 +36,7 @@ import { createReactive } from "../lib/reactive.js";
34
36
  import { getStageEmoji } from "../formatters/stage/shared.js";
35
37
  import { formatAgentProfile } from "../formatters/agent/profile.js";
36
38
  import { formatAgentSkill } from "../formatters/agent/skill.js";
39
+ import { createCodeDisplay } from "../components/code-display.js";
37
40
 
38
41
  /** All stages option value */
39
42
  const ALL_STAGES_VALUE = "all";
@@ -101,9 +104,71 @@ export async function renderAgentBuilder() {
101
104
  const availableDisciplines = data.disciplines.filter((d) =>
102
105
  agentDisciplineIds.has(d.id),
103
106
  );
104
- const availableTracks = data.tracks.filter((t) => agentTrackIds.has(t.id));
107
+ // All tracks with agent definitions (will be filtered per-discipline)
108
+ const allAgentTracks = data.tracks.filter((t) => agentTrackIds.has(t.id));
105
109
  const stages = data.stages || [];
106
110
 
111
+ /**
112
+ * Get tracks valid for a discipline that also have agent definitions
113
+ * @param {string} disciplineId - Discipline ID
114
+ * @returns {Array} - Valid tracks for the discipline
115
+ */
116
+ function getValidTracksForDiscipline(disciplineId) {
117
+ const discipline = data.disciplines.find((d) => d.id === disciplineId);
118
+ if (!discipline) return [];
119
+
120
+ const validTracks = discipline.validTracks ?? [];
121
+ // Filter to track IDs only (exclude null which means trackless)
122
+ const validTrackIds = validTracks.filter((t) => t !== null);
123
+
124
+ // Intersection: valid for discipline AND has agent definition
125
+ return allAgentTracks.filter((t) => validTrackIds.includes(t.id));
126
+ }
127
+
128
+ // Track select element - created once, options updated when discipline changes
129
+ const trackSelectEl = select(
130
+ { className: "form-select", id: "agent-track-select" },
131
+ option({ value: "", disabled: true, selected: true }, "Select a track..."),
132
+ );
133
+ trackSelectEl.disabled = true;
134
+
135
+ /**
136
+ * Update track select options based on selected discipline
137
+ * @param {string} disciplineId - Discipline ID
138
+ */
139
+ function updateTrackOptions(disciplineId) {
140
+ const validTracks = getValidTracksForDiscipline(disciplineId);
141
+
142
+ // Clear existing options
143
+ trackSelectEl.innerHTML = "";
144
+
145
+ if (validTracks.length === 0) {
146
+ trackSelectEl.appendChild(
147
+ option(
148
+ { value: "", disabled: true, selected: true },
149
+ "No tracks available for this discipline",
150
+ ),
151
+ );
152
+ trackSelectEl.disabled = true;
153
+ return;
154
+ }
155
+
156
+ // Add placeholder
157
+ trackSelectEl.appendChild(
158
+ option(
159
+ { value: "", disabled: true, selected: true },
160
+ "Select a track...",
161
+ ),
162
+ );
163
+
164
+ // Add available track options
165
+ validTracks.forEach((t) => {
166
+ trackSelectEl.appendChild(option({ value: t.id }, t.name));
167
+ });
168
+
169
+ trackSelectEl.disabled = false;
170
+ }
171
+
107
172
  // Build stage options with "All Stages" first
108
173
  const stageOptions = [
109
174
  { id: ALL_STAGES_VALUE, name: "All Stages" },
@@ -134,7 +199,7 @@ export async function renderAgentBuilder() {
134
199
  // Preview container - will be updated reactively
135
200
  const previewContainer = div(
136
201
  { className: "agent-preview" },
137
- createEmptyState(availableDisciplines.length, availableTracks.length),
202
+ createEmptyState(availableDisciplines.length, allAgentTracks.length),
138
203
  );
139
204
 
140
205
  /**
@@ -156,7 +221,7 @@ export async function renderAgentBuilder() {
156
221
 
157
222
  if (!discipline) {
158
223
  previewContainer.appendChild(
159
- createEmptyState(availableDisciplines.length, availableTracks.length),
224
+ createEmptyState(availableDisciplines.length, allAgentTracks.length),
160
225
  );
161
226
  return;
162
227
  }
@@ -251,7 +316,14 @@ export async function renderAgentBuilder() {
251
316
  initialValue: selection.get().discipline,
252
317
  placeholder: "Select a discipline...",
253
318
  onChange: (value) => {
254
- selection.update((prev) => ({ ...prev, discipline: value }));
319
+ // Update track options when discipline changes
320
+ updateTrackOptions(value);
321
+ // Reset track selection when discipline changes
322
+ selection.update((prev) => ({
323
+ ...prev,
324
+ discipline: value,
325
+ track: "",
326
+ }));
255
327
  },
256
328
  getDisplayName: (d) => d.specialization || d.name,
257
329
  })
@@ -260,25 +332,32 @@ export async function renderAgentBuilder() {
260
332
  "No disciplines have agent definitions.",
261
333
  ),
262
334
  ),
263
- // Track selector
335
+ // Track selector (dynamically filtered by discipline)
264
336
  div(
265
337
  { className: "form-group" },
266
338
  label({ className: "form-label" }, "Track"),
267
- availableTracks.length > 0
268
- ? createSelectWithValue({
269
- id: "agent-track-select",
270
- items: availableTracks,
271
- initialValue: selection.get().track,
272
- placeholder: "Select a track...",
273
- onChange: (value) => {
274
- selection.update((prev) => ({ ...prev, track: value }));
275
- },
276
- getDisplayName: (t) => t.name,
277
- })
278
- : p(
279
- { className: "text-muted" },
280
- "No tracks have agent definitions.",
281
- ),
339
+ (() => {
340
+ // Wire up track select change handler
341
+ trackSelectEl.addEventListener("change", (e) => {
342
+ selection.update((prev) => ({ ...prev, track: e.target.value }));
343
+ });
344
+ // Initialize track options if discipline is pre-selected
345
+ const initialDiscipline = selection.get().discipline;
346
+ if (initialDiscipline) {
347
+ updateTrackOptions(initialDiscipline);
348
+ // Set initial track value if provided and valid
349
+ const initialTrack = selection.get().track;
350
+ const validTracks =
351
+ getValidTracksForDiscipline(initialDiscipline);
352
+ if (
353
+ initialTrack &&
354
+ validTracks.some((t) => t.id === initialTrack)
355
+ ) {
356
+ trackSelectEl.value = initialTrack;
357
+ }
358
+ }
359
+ return trackSelectEl;
360
+ })(),
282
361
  ),
283
362
  // Stage selector (dropdown with All Stages option)
284
363
  div(
@@ -590,10 +669,15 @@ function createAgentCard(stage, profile, stages, agentTemplate, _derived) {
590
669
  span({ className: "agent-card-emoji" }, stageEmoji),
591
670
  h3({}, `${stage.name} Agent`),
592
671
  ),
593
- createCopyButton(content),
594
672
  ),
595
- p({ className: "agent-card-filename" }, profile.filename),
596
- div({ className: "agent-card-preview" }, createCodePreview(content)),
673
+ div(
674
+ { className: "agent-card-preview" },
675
+ createCodeDisplay({
676
+ content,
677
+ filename: profile.filename,
678
+ maxHeight: 400,
679
+ }),
680
+ ),
597
681
  );
598
682
 
599
683
  return card;
@@ -614,57 +698,18 @@ function createSkillCard(skill, skillTemplate) {
614
698
  div(
615
699
  { className: "skill-card-header" },
616
700
  span({ className: "skill-card-name" }, skill.frontmatter.name),
617
- createCopyButton(content),
618
701
  ),
619
- p({ className: "skill-card-filename" }, filename),
620
- div({ className: "skill-card-preview" }, createCodePreview(content)),
702
+ div(
703
+ { className: "skill-card-preview" },
704
+ createCodeDisplay({
705
+ content,
706
+ filename,
707
+ maxHeight: 300,
708
+ }),
709
+ ),
621
710
  );
622
711
  }
623
712
 
624
- /**
625
- * Create a code preview element
626
- * @param {string} content - Code content
627
- * @returns {HTMLElement}
628
- */
629
- function createCodePreview(content) {
630
- const pre = document.createElement("pre");
631
- pre.className = "code-block code-preview";
632
-
633
- const code = document.createElement("code");
634
- code.textContent = content;
635
-
636
- pre.appendChild(code);
637
- return pre;
638
- }
639
-
640
- /**
641
- * Create a copy button
642
- * @param {string} content - Content to copy
643
- * @returns {HTMLElement}
644
- */
645
- function createCopyButton(content) {
646
- const btn = document.createElement("button");
647
- btn.className = "btn btn-sm copy-btn";
648
- btn.textContent = "πŸ“‹ Copy";
649
-
650
- btn.addEventListener("click", async () => {
651
- try {
652
- await navigator.clipboard.writeText(content);
653
- btn.textContent = "βœ“ Copied";
654
- setTimeout(() => {
655
- btn.textContent = "πŸ“‹ Copy";
656
- }, 2000);
657
- } catch {
658
- btn.textContent = "Failed";
659
- setTimeout(() => {
660
- btn.textContent = "πŸ“‹ Copy";
661
- }, 2000);
662
- }
663
- });
664
-
665
- return btn;
666
- }
667
-
668
713
  /**
669
714
  * Create download all button for all stages
670
715
  * @param {Array} stageAgents - Array of {stage, derived, profile}
@@ -860,11 +905,10 @@ function createCliHint(disciplineId, trackId, stageId) {
860
905
  { className: "agent-section cli-hint" },
861
906
  h2({}, "CLI Alternative"),
862
907
  p({}, "Generate this agent from the command line:"),
863
- div(
864
- { className: "cli-command" },
865
- createCodePreview(command),
866
- createCopyButton(command),
867
- ),
908
+ createCodeDisplay({
909
+ content: command,
910
+ language: "bash",
911
+ }),
868
912
  );
869
913
 
870
914
  return container;
@@ -53,7 +53,7 @@ export function renderLanding() {
53
53
  { className: "landing-hero" },
54
54
  div(
55
55
  { className: "hero-title-wrapper" },
56
- h1({}, `${framework.emoji} ${framework.title}`),
56
+ h1({}, `${framework.emojiIcon} ${framework.title}`),
57
57
  span({ className: "brand-tag brand-tag-hero" }, framework.tag),
58
58
  ),
59
59
  p({}, framework.description.trim()),
@@ -11,12 +11,12 @@ import { getConceptEmoji } from "../model/levels.js";
11
11
 
12
12
  /**
13
13
  * Map stage to card configuration
14
- * @param {Object} stage - Prepared stage item (includes emoji)
14
+ * @param {Object} stage - Prepared stage item (includes emojiIcon)
15
15
  * @returns {Object} Card configuration
16
16
  */
17
17
  function stageToCardConfig(stage) {
18
18
  return {
19
- title: `${stage.emoji} ${stage.name}`,
19
+ title: `${stage.emojiIcon} ${stage.name}`,
20
20
  description: stage.truncatedDescription,
21
21
  href: `/stage/${stage.id}`,
22
22
  };
@@ -24,7 +24,7 @@ function stageToCardConfig(stage) {
24
24
 
25
25
  /**
26
26
  * Create lifecycle flow visualization
27
- * @param {Array} stages - Array of stage items (each includes emoji)
27
+ * @param {Array} stages - Array of stage items (each includes emojiIcon)
28
28
  * @returns {HTMLElement}
29
29
  */
30
30
  function createLifecycleFlow(stages) {
@@ -35,7 +35,7 @@ function createLifecycleFlow(stages) {
35
35
  { className: "lifecycle-flow-item" },
36
36
  a(
37
37
  { href: `#/stage/${stage.id}`, className: "lifecycle-stage" },
38
- span({ className: "lifecycle-emoji" }, stage.emoji),
38
+ span({ className: "lifecycle-emoji" }, stage.emojiIcon),
39
39
  span({ className: "lifecycle-name" }, stage.name),
40
40
  ),
41
41
  !isLast ? span({ className: "lifecycle-arrow" }, "β†’") : null,
package/app/slide-main.js CHANGED
@@ -346,7 +346,7 @@ function populateBrandHeader(framework) {
346
346
  header.appendChild(
347
347
  a(
348
348
  { className: "brand-title", href: "#/" },
349
- `${framework.emoji} ${framework.title}`,
349
+ `${framework.emojiIcon} ${framework.title}`,
350
350
  ),
351
351
  );
352
352
  header.appendChild(span({ className: "brand-tag" }, framework.tag));
@@ -20,37 +20,37 @@ export function renderChapterSlide({ render, data, params }) {
20
20
  const chapterConfig = {
21
21
  driver: {
22
22
  title: framework.entityDefinitions.driver.title,
23
- emoji: framework.entityDefinitions.driver.emoji,
23
+ emojiIcon: framework.entityDefinitions.driver.emojiIcon,
24
24
  description: framework.entityDefinitions.driver.description,
25
25
  },
26
26
  skill: {
27
27
  title: framework.entityDefinitions.skill.title,
28
- emoji: framework.entityDefinitions.skill.emoji,
28
+ emojiIcon: framework.entityDefinitions.skill.emojiIcon,
29
29
  description: framework.entityDefinitions.skill.description,
30
30
  },
31
31
  behaviour: {
32
32
  title: framework.entityDefinitions.behaviour.title,
33
- emoji: framework.entityDefinitions.behaviour.emoji,
33
+ emojiIcon: framework.entityDefinitions.behaviour.emojiIcon,
34
34
  description: framework.entityDefinitions.behaviour.description,
35
35
  },
36
36
  discipline: {
37
37
  title: framework.entityDefinitions.discipline.title,
38
- emoji: framework.entityDefinitions.discipline.emoji,
38
+ emojiIcon: framework.entityDefinitions.discipline.emojiIcon,
39
39
  description: framework.entityDefinitions.discipline.description,
40
40
  },
41
41
  grade: {
42
42
  title: framework.entityDefinitions.grade.title,
43
- emoji: framework.entityDefinitions.grade.emoji,
43
+ emojiIcon: framework.entityDefinitions.grade.emojiIcon,
44
44
  description: framework.entityDefinitions.grade.description,
45
45
  },
46
46
  track: {
47
47
  title: framework.entityDefinitions.track.title,
48
- emoji: framework.entityDefinitions.track.emoji,
48
+ emojiIcon: framework.entityDefinitions.track.emojiIcon,
49
49
  description: framework.entityDefinitions.track.description,
50
50
  },
51
51
  job: {
52
52
  title: framework.entityDefinitions.job.title,
53
- emoji: framework.entityDefinitions.job.emoji,
53
+ emojiIcon: framework.entityDefinitions.job.emojiIcon,
54
54
  description: framework.entityDefinitions.job.description,
55
55
  },
56
56
  };
@@ -72,7 +72,7 @@ export function renderChapterSlide({ render, data, params }) {
72
72
  { className: "slide chapter-cover" },
73
73
  h1(
74
74
  { className: "chapter-title" },
75
- config.emoji ? `${config.emoji} ` : "",
75
+ config.emojiIcon ? `${config.emojiIcon} ` : "",
76
76
  span({ className: "gradient-text" }, config.title),
77
77
  ),
78
78
  p({ className: "chapter-description" }, config.description.trim()),
@@ -22,7 +22,7 @@ export function renderSlideIndex({ render, data }) {
22
22
  { className: "page-header" },
23
23
  heading1(
24
24
  { className: "page-title" },
25
- `${framework.emoji} ${framework.title}`,
25
+ `${framework.emojiIcon} ${framework.title}`,
26
26
  ),
27
27
  p(
28
28
  { className: "page-description" },
@@ -63,14 +63,14 @@ export function renderOverviewSlide({ render, data, params }) {
63
63
  const chapterConfig = {
64
64
  driver: {
65
65
  title: framework.entityDefinitions.driver.title,
66
- emoji: framework.entityDefinitions.driver.emoji,
66
+ emojiIcon: framework.entityDefinitions.driver.emojiIcon,
67
67
  description: framework.entityDefinitions.driver.description,
68
68
  entities: prepareDriversList(data.drivers).items,
69
69
  mapper: driverToCardConfig,
70
70
  },
71
71
  skill: {
72
72
  title: framework.entityDefinitions.skill.title,
73
- emoji: framework.entityDefinitions.skill.emoji,
73
+ emojiIcon: framework.entityDefinitions.skill.emojiIcon,
74
74
  description: framework.entityDefinitions.skill.description,
75
75
  entities: Object.values(
76
76
  prepareSkillsList(data.skills, data.capabilities).groups,
@@ -79,14 +79,14 @@ export function renderOverviewSlide({ render, data, params }) {
79
79
  },
80
80
  behaviour: {
81
81
  title: framework.entityDefinitions.behaviour.title,
82
- emoji: framework.entityDefinitions.behaviour.emoji,
82
+ emojiIcon: framework.entityDefinitions.behaviour.emojiIcon,
83
83
  description: framework.entityDefinitions.behaviour.description,
84
84
  entities: prepareBehavioursList(data.behaviours).items,
85
85
  mapper: behaviourToCardConfig,
86
86
  },
87
87
  discipline: {
88
88
  title: framework.entityDefinitions.discipline.title,
89
- emoji: framework.entityDefinitions.discipline.emoji,
89
+ emojiIcon: framework.entityDefinitions.discipline.emojiIcon,
90
90
  description: framework.entityDefinitions.discipline.description,
91
91
  groups: prepareDisciplinesList(data.disciplines).groups,
92
92
  mapper: disciplineToCardConfig,
@@ -94,21 +94,21 @@ export function renderOverviewSlide({ render, data, params }) {
94
94
  },
95
95
  grade: {
96
96
  title: framework.entityDefinitions.grade.title,
97
- emoji: framework.entityDefinitions.grade.emoji,
97
+ emojiIcon: framework.entityDefinitions.grade.emojiIcon,
98
98
  description: framework.entityDefinitions.grade.description,
99
99
  entities: prepareGradesList(data.grades).items,
100
100
  mapper: gradeToCardConfig,
101
101
  },
102
102
  track: {
103
103
  title: framework.entityDefinitions.track.title,
104
- emoji: framework.entityDefinitions.track.emoji,
104
+ emojiIcon: framework.entityDefinitions.track.emojiIcon,
105
105
  description: framework.entityDefinitions.track.description,
106
106
  entities: prepareTracksList(data.tracks).items,
107
107
  mapper: trackToCardConfig,
108
108
  },
109
109
  job: {
110
110
  title: framework.entityDefinitions.job.title,
111
- emoji: framework.entityDefinitions.job.emoji,
111
+ emojiIcon: framework.entityDefinitions.job.emojiIcon,
112
112
  description: framework.entityDefinitions.job.description,
113
113
  entities: generateAllJobs({
114
114
  disciplines: data.disciplines,
@@ -150,7 +150,7 @@ export function renderOverviewSlide({ render, data, params }) {
150
150
  { className: "overview-header" },
151
151
  h1(
152
152
  { className: "overview-title" },
153
- config.emoji ? `${config.emoji} ` : "",
153
+ config.emojiIcon ? `${config.emojiIcon} ` : "",
154
154
  span({ className: "gradient-text" }, config.title),
155
155
  ),
156
156
  p({ className: "overview-description" }, config.description.trim()),
@@ -35,6 +35,7 @@ export function renderSkillSlide({ render, data, params }) {
35
35
  drivers: data.drivers,
36
36
  capabilities: data.capabilities,
37
37
  showBackLink: false,
38
+ showToolsAndPatterns: false,
38
39
  }),
39
40
  );
40
41
  }
@@ -1,7 +1,7 @@
1
1
  # yaml-language-server: $schema=https://schema.forwardimpact.team/json/capability.schema.json
2
2
 
3
3
  name: Business
4
- emoji: πŸ’Ό
4
+ emojiIcon: πŸ’Ό
5
5
  displayOrder: 8
6
6
  description: |
7
7
  Understanding and driving business value.
@@ -1,7 +1,7 @@
1
1
  # yaml-language-server: $schema=https://schema.forwardimpact.team/json/capability.schema.json
2
2
 
3
3
  name: Delivery
4
- emoji: πŸš€
4
+ emojiIcon: πŸš€
5
5
  displayOrder: 1
6
6
  description: |
7
7
  Building and shipping solutions that solve real problems.
@@ -271,6 +271,7 @@ skills:
271
271
  toolReferences:
272
272
  - name: Terraform
273
273
  url: https://developer.hashicorp.com/terraform/docs
274
+ simpleIcon: terraform
274
275
  description: Infrastructure as code tool
275
276
  useWhen: Provisioning and managing cloud infrastructure
276
277
  - name: CloudFormation
@@ -279,6 +280,7 @@ skills:
279
280
  useWhen: Managing AWS infrastructure as code
280
281
  - name: Docker
281
282
  url: https://docs.docker.com/
283
+ simpleIcon: docker
282
284
  description: Container platform
283
285
  useWhen: Containerizing applications or managing container environments
284
286
  implementationReference: |
@@ -1,7 +1,7 @@
1
1
  # yaml-language-server: $schema=https://schema.forwardimpact.team/json/capability.schema.json
2
2
 
3
3
  name: People
4
- emoji: πŸ‘₯
4
+ emojiIcon: πŸ‘₯
5
5
  displayOrder: 6
6
6
  description: |
7
7
  Growing individuals and building effective teams.
@@ -1,7 +1,7 @@
1
1
  # yaml-language-server: $schema=https://schema.forwardimpact.team/json/capability.schema.json
2
2
 
3
3
  name: Reliability
4
- emoji: πŸ›‘οΈ
4
+ emojiIcon: πŸ›‘οΈ
5
5
  displayOrder: 5
6
6
  description: |
7
7
  Ensuring systems are dependable, secure, and observable.
@@ -158,10 +158,12 @@ skills:
158
158
  toolReferences:
159
159
  - name: Terraform
160
160
  url: https://developer.hashicorp.com/terraform/docs
161
+ simpleIcon: terraform
161
162
  description: Infrastructure as code tool
162
163
  useWhen: Provisioning and managing cloud infrastructure
163
164
  - name: Docker
164
165
  url: https://docs.docker.com/
166
+ simpleIcon: docker
165
167
  description: Container platform
166
168
  useWhen: Containerizing applications or managing container environments
167
169
  implementationReference: |
@@ -1,7 +1,7 @@
1
1
  # yaml-language-server: $schema=https://schema.forwardimpact.team/json/capability.schema.json
2
2
 
3
3
  name: Scale
4
- emoji: πŸ“
4
+ emojiIcon: πŸ“
5
5
  displayOrder: 4
6
6
  description: |
7
7
  Building systems that grow gracefully.
@@ -1,7 +1,7 @@
1
1
  # yaml-language-server: $schema=https://schema.forwardimpact.team/json/framework.schema.json
2
2
 
3
3
  title: Engineering Pathway
4
- emoji: "🧭"
4
+ emojiIcon: "🧭"
5
5
  tag: "#AcmeCorp"
6
6
  description: |
7
7
  A unified framework for human and AI collaboration in engineering. Define roles, track skills and behaviours, build career paths, and generate AI coding agentsβ€”all from the same coherent foundation. The pathway aligns human capabilities with AI assistance, enabling productive teams in the AI era.
@@ -10,60 +10,60 @@ description: |
10
10
  entityDefinitions:
11
11
  driver:
12
12
  title: Drivers
13
- emoji: "🎯"
13
+ emojiIcon: "🎯"
14
14
  description: |
15
15
  Organizational outcomes that productive teams achieve. Drivers link skills and behaviours to business value, helping teams understand how their capabilities contribute to organizational success.
16
16
 
17
17
  skill:
18
18
  title: Skills
19
- emoji: "πŸ’‘"
19
+ emojiIcon: "πŸ’‘"
20
20
  description: |
21
21
  Capabilities required to perform work effectively. Skills are organized by capability and measured across five levels from Awareness to Expert. They form the foundation of technical and professional competence.
22
22
 
23
23
  behaviour:
24
24
  title: Behaviours
25
- emoji: "🧠"
25
+ emojiIcon: "🧠"
26
26
  description: |
27
27
  Mindsets and ways of working that underpin effective application of skills. Behaviours are measured across five maturity levels from Emerging to Exemplifying. They describe how engineers approach their work, collaborate with others, and drive outcomes.
28
28
 
29
29
  discipline:
30
30
  title: Disciplines
31
- emoji: "πŸ”§"
31
+ emojiIcon: "πŸ”§"
32
32
  description: |
33
33
  Engineering specializations that define T-shaped skill profiles. Each discipline specifies primary skills for deep expertise, secondary skills for supporting capabilities, and broad skills for general awareness. Disciplines answer the question: "What kind of engineer are you?"
34
34
 
35
35
  grade:
36
36
  title: Grades
37
- emoji: "πŸ“Š"
37
+ emojiIcon: "πŸ“Š"
38
38
  description: |
39
39
  Career levels that define expectations for skill proficiency, behaviour maturity, and scope of impact. Grades provide a clear progression path from entry-level to distinguished leadership roles, with defined expectations at each stage.
40
40
 
41
41
  track:
42
42
  title: Tracks
43
- emoji: "πŸ›€οΈ"
43
+ emojiIcon: "πŸ›€οΈ"
44
44
  description: |
45
45
  Work contexts that modify skill and behaviour expectations based on the nature of the role. Tracks answer the question: "Where and how do you work?" They apply modifiers to adjust expectations for different working environments and responsibilities.
46
46
 
47
47
  job:
48
48
  title: Jobs
49
- emoji: "πŸ“‹"
49
+ emojiIcon: "πŸ“‹"
50
50
  description: |
51
51
  Complete role specifications that combine discipline, track, and grade into actionable job definitions with skill matrices, behaviour profiles, and derived responsibilities. Jobs represent the practical application of the framework to define concrete engineering positions.
52
52
 
53
53
  agent:
54
54
  title: Agents
55
- emoji: "πŸ€–"
55
+ emojiIcon: "πŸ€–"
56
56
  description: |
57
57
  AI coding agent configurations generated from the same disciplines, tracks, and skills that define human roles. Agent profiles and skills follow the GitHub Copilot specification, ensuring AI assistants understand context, constraints, and working styles aligned with human engineering expectations.
58
58
 
59
59
  stage:
60
60
  title: Stages
61
- emoji: "πŸ”„"
61
+ emojiIcon: "πŸ”„"
62
62
  description: |
63
63
  Defined phases of the engineering lifecycle that structure work from planning through delivery and review. Stages provide context for AI agents and define entry criteria, exit criteria, and appropriate handoffs between phases.
64
64
 
65
65
  tool:
66
66
  title: Tools
67
- emoji: "πŸ”§"
67
+ emojiIcon: "πŸ”§"
68
68
  description: |
69
69
  Recommended tools and utilities referenced by skills for effective engineering workflows. Tools are linked to specific skills with guidance on when and how to use them, surfacing best practices directly in skill definitions.