agentblueprint 0.6.20 → 0.6.22
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/dist/__tests__/renderers.test.js +520 -2
- package/dist/__tests__/renderers.test.js.map +1 -1
- package/dist/__tests__/tools.test.js +2 -2
- package/dist/download.js +17 -1
- package/dist/download.js.map +1 -1
- package/dist/renderers.d.ts +4 -0
- package/dist/renderers.js +724 -28
- package/dist/renderers.js.map +1 -1
- package/dist/tools/download-blueprint.js +17 -3
- package/dist/tools/download-blueprint.js.map +1 -1
- package/package.json +1 -1
|
@@ -182,7 +182,9 @@ describe('renderSkillDirectory', () => {
|
|
|
182
182
|
expect(paths).toContain('GETTING-STARTED.md');
|
|
183
183
|
expect(paths).toContain('scripts/validate-spec.sh');
|
|
184
184
|
expect(paths).toContain('implementation-state.yaml');
|
|
185
|
-
expect(
|
|
185
|
+
expect(paths).toContain('AGENTS.md');
|
|
186
|
+
expect(paths).toContain('hooks/claude-code-sync.json');
|
|
187
|
+
expect(files.size).toBe(15);
|
|
186
188
|
});
|
|
187
189
|
it('SKILL.md starts with YAML frontmatter', () => {
|
|
188
190
|
const files = renderSkillDirectory(minimalInput);
|
|
@@ -379,7 +381,7 @@ describe('renderSkillDirectory with missing data', () => {
|
|
|
379
381
|
};
|
|
380
382
|
// Should not throw
|
|
381
383
|
const files = renderSkillDirectory(input);
|
|
382
|
-
expect(files.size).toBe(
|
|
384
|
+
expect(files.size).toBe(15);
|
|
383
385
|
});
|
|
384
386
|
it('SKILL.md renders phases from implementation plan epics', () => {
|
|
385
387
|
const input = {
|
|
@@ -677,6 +679,114 @@ describe('GETTING-STARTED.md implementation state reference', () => {
|
|
|
677
679
|
expect(guide).toContain('agentblueprint sync');
|
|
678
680
|
});
|
|
679
681
|
});
|
|
682
|
+
describe('GETTING-STARTED.md sync enhancements', () => {
|
|
683
|
+
it('Step 5 has sync trigger points', () => {
|
|
684
|
+
const files = renderSkillDirectory(minimalInput);
|
|
685
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
686
|
+
expect(guide).toContain('After implementing an agent');
|
|
687
|
+
expect(guide).toContain('After connecting an integration');
|
|
688
|
+
});
|
|
689
|
+
it('Step 5 has MCP tool examples', () => {
|
|
690
|
+
const files = renderSkillDirectory(minimalInput);
|
|
691
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
692
|
+
expect(guide).toContain('sync_implementation_state');
|
|
693
|
+
expect(guide).toContain('report_metric');
|
|
694
|
+
});
|
|
695
|
+
it('Step 5 references AGENTS.md', () => {
|
|
696
|
+
const files = renderSkillDirectory(minimalInput);
|
|
697
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
698
|
+
expect(guide).toContain('AGENTS.md');
|
|
699
|
+
});
|
|
700
|
+
it('Step 5 references hooks/claude-code-sync.json', () => {
|
|
701
|
+
const files = renderSkillDirectory(minimalInput);
|
|
702
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
703
|
+
expect(guide).toContain('hooks/claude-code-sync.json');
|
|
704
|
+
});
|
|
705
|
+
it('Step 5 includes the blueprint ID in tool examples', () => {
|
|
706
|
+
const files = renderSkillDirectory(minimalInput);
|
|
707
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
708
|
+
expect(guide).toContain('bp-123');
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
describe('AGENTS.md', () => {
|
|
712
|
+
it('contains sync trigger points', () => {
|
|
713
|
+
const files = renderSkillDirectory(minimalInput);
|
|
714
|
+
const agents = files.get('AGENTS.md');
|
|
715
|
+
expect(agents).toContain('After implementing an agent');
|
|
716
|
+
expect(agents).toContain('At the end of every coding session');
|
|
717
|
+
});
|
|
718
|
+
it('contains MCP tool examples', () => {
|
|
719
|
+
const files = renderSkillDirectory(minimalInput);
|
|
720
|
+
const agents = files.get('AGENTS.md');
|
|
721
|
+
expect(agents).toContain('sync_implementation_state');
|
|
722
|
+
expect(agents).toContain('report_metric');
|
|
723
|
+
});
|
|
724
|
+
it('contains CLI examples', () => {
|
|
725
|
+
const files = renderSkillDirectory(minimalInput);
|
|
726
|
+
const agents = files.get('AGENTS.md');
|
|
727
|
+
expect(agents).toContain('agentblueprint sync');
|
|
728
|
+
});
|
|
729
|
+
it('includes the blueprint ID', () => {
|
|
730
|
+
const files = renderSkillDirectory(minimalInput);
|
|
731
|
+
const agents = files.get('AGENTS.md');
|
|
732
|
+
expect(agents).toContain('bp-123');
|
|
733
|
+
});
|
|
734
|
+
it('contains deviation documentation guidance', () => {
|
|
735
|
+
const files = renderSkillDirectory(minimalInput);
|
|
736
|
+
const agents = files.get('AGENTS.md');
|
|
737
|
+
expect(agents).toContain('deviations');
|
|
738
|
+
expect(agents).toContain('implementation-state.yaml');
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
describe('hooks/claude-code-sync.json', () => {
|
|
742
|
+
it('is valid JSON', () => {
|
|
743
|
+
const files = renderSkillDirectory(minimalInput);
|
|
744
|
+
const hookContent = files.get('hooks/claude-code-sync.json');
|
|
745
|
+
expect(() => JSON.parse(hookContent)).not.toThrow();
|
|
746
|
+
});
|
|
747
|
+
it('has Stop hook structure', () => {
|
|
748
|
+
const files = renderSkillDirectory(minimalInput);
|
|
749
|
+
const config = JSON.parse(files.get('hooks/claude-code-sync.json'));
|
|
750
|
+
expect(config.hooks).toBeDefined();
|
|
751
|
+
expect(config.hooks.Stop).toBeDefined();
|
|
752
|
+
expect(Array.isArray(config.hooks.Stop)).toBe(true);
|
|
753
|
+
expect(config.hooks.Stop[0].hooks[0].type).toBe('command');
|
|
754
|
+
});
|
|
755
|
+
it('does not include matcher field (Stop hooks do not support matchers)', () => {
|
|
756
|
+
const files = renderSkillDirectory(minimalInput);
|
|
757
|
+
const config = JSON.parse(files.get('hooks/claude-code-sync.json'));
|
|
758
|
+
expect(config.hooks.Stop[0]).not.toHaveProperty('matcher');
|
|
759
|
+
});
|
|
760
|
+
it('checks stop_hook_active to prevent infinite loops', () => {
|
|
761
|
+
const files = renderSkillDirectory(minimalInput);
|
|
762
|
+
const config = JSON.parse(files.get('hooks/claude-code-sync.json'));
|
|
763
|
+
const command = config.hooks.Stop[0].hooks[0].command;
|
|
764
|
+
expect(command).toContain('stop_hook_active');
|
|
765
|
+
});
|
|
766
|
+
it('uses jq for JSON parsing', () => {
|
|
767
|
+
const files = renderSkillDirectory(minimalInput);
|
|
768
|
+
const config = JSON.parse(files.get('hooks/claude-code-sync.json'));
|
|
769
|
+
const command = config.hooks.Stop[0].hooks[0].command;
|
|
770
|
+
expect(command).toContain('jq');
|
|
771
|
+
});
|
|
772
|
+
it('uses git status --porcelain to catch untracked files', () => {
|
|
773
|
+
const files = renderSkillDirectory(minimalInput);
|
|
774
|
+
const config = JSON.parse(files.get('hooks/claude-code-sync.json'));
|
|
775
|
+
const command = config.hooks.Stop[0].hooks[0].command;
|
|
776
|
+
expect(command).toContain('git status --porcelain');
|
|
777
|
+
});
|
|
778
|
+
it('includes the blueprint ID', () => {
|
|
779
|
+
const files = renderSkillDirectory(minimalInput);
|
|
780
|
+
const config = JSON.parse(files.get('hooks/claude-code-sync.json'));
|
|
781
|
+
const command = config.hooks.Stop[0].hooks[0].command;
|
|
782
|
+
expect(command).toContain('bp-123');
|
|
783
|
+
});
|
|
784
|
+
it('has a 30-second timeout', () => {
|
|
785
|
+
const files = renderSkillDirectory(minimalInput);
|
|
786
|
+
const config = JSON.parse(files.get('hooks/claude-code-sync.json'));
|
|
787
|
+
expect(config.hooks.Stop[0].hooks[0].timeout).toBe(30);
|
|
788
|
+
});
|
|
789
|
+
});
|
|
680
790
|
describe('validate-spec.sh implementation state check', () => {
|
|
681
791
|
it('checks for implementation-state.yaml', () => {
|
|
682
792
|
const files = renderSkillDirectory(minimalInput);
|
|
@@ -684,4 +794,412 @@ describe('validate-spec.sh implementation state check', () => {
|
|
|
684
794
|
expect(script).toContain('check_optional "implementation-state.yaml"');
|
|
685
795
|
});
|
|
686
796
|
});
|
|
797
|
+
// =============================================================================
|
|
798
|
+
// Reality Layer Tests (Living Blueprint Phase 3A)
|
|
799
|
+
// =============================================================================
|
|
800
|
+
const sampleImplementationState = {
|
|
801
|
+
id: 'state-1',
|
|
802
|
+
blueprintId: 'bp-123',
|
|
803
|
+
organizationId: 'org-1',
|
|
804
|
+
stateData: {
|
|
805
|
+
schema_version: '1.0',
|
|
806
|
+
blueprint_id: 'bp-123',
|
|
807
|
+
last_updated: '2026-03-20T12:00:00Z',
|
|
808
|
+
overall_status: 'in_progress',
|
|
809
|
+
platform: { name: 'ServiceNow', version: 'Australia', environment: 'dev' },
|
|
810
|
+
agents: [
|
|
811
|
+
{
|
|
812
|
+
name: 'Test Agent',
|
|
813
|
+
status: 'implemented',
|
|
814
|
+
platform_artifact: 'sys_id:abc123',
|
|
815
|
+
deviations: ['Used Flow Designer instead of Workflow'],
|
|
816
|
+
integrations_connected: ['CMDB', 'Service Portal'],
|
|
817
|
+
notes: 'Working well',
|
|
818
|
+
},
|
|
819
|
+
],
|
|
820
|
+
architecture: {
|
|
821
|
+
pattern: 'Supervisor',
|
|
822
|
+
deviations: [],
|
|
823
|
+
additional_components: ['Custom REST API'],
|
|
824
|
+
},
|
|
825
|
+
metrics_observed: [],
|
|
826
|
+
},
|
|
827
|
+
schemaVersion: '1.0',
|
|
828
|
+
syncedAt: '2026-03-20T12:00:00Z',
|
|
829
|
+
syncedBy: 'cli',
|
|
830
|
+
previousStateId: null,
|
|
831
|
+
};
|
|
832
|
+
const sampleProgress = {
|
|
833
|
+
blueprintId: 'bp-123',
|
|
834
|
+
blueprintTitle: 'Test Blueprint',
|
|
835
|
+
targets: {
|
|
836
|
+
operational: [{ name: 'Test Pass Rate', target: '99%', unit: '%', direction: 'higher_is_better' }],
|
|
837
|
+
financial: [{ name: 'ROI', value: '285%', unit: '%', direction: 'higher_is_better' }],
|
|
838
|
+
},
|
|
839
|
+
actuals: [
|
|
840
|
+
{
|
|
841
|
+
id: 'metric-1',
|
|
842
|
+
metricName: 'Test Pass Rate',
|
|
843
|
+
metricType: 'operational',
|
|
844
|
+
predictedValue: '99%',
|
|
845
|
+
actualValue: '94%',
|
|
846
|
+
deviationPercent: -5.05,
|
|
847
|
+
status: 'minor_deviation',
|
|
848
|
+
recordedAt: '2026-03-18T10:00:00Z',
|
|
849
|
+
dataSource: 'manual',
|
|
850
|
+
recordingCount: 3,
|
|
851
|
+
},
|
|
852
|
+
],
|
|
853
|
+
summary: {
|
|
854
|
+
totalTargets: 2,
|
|
855
|
+
metricsRecorded: 1,
|
|
856
|
+
onTrack: 0,
|
|
857
|
+
minorDeviation: 1,
|
|
858
|
+
majorDeviation: 0,
|
|
859
|
+
},
|
|
860
|
+
implementationState: {
|
|
861
|
+
overallStatus: 'in_progress',
|
|
862
|
+
agentCount: 1,
|
|
863
|
+
implementedCount: 1,
|
|
864
|
+
lastSyncedAt: '2026-03-20T12:00:00Z',
|
|
865
|
+
},
|
|
866
|
+
};
|
|
867
|
+
const twoAgentState = {
|
|
868
|
+
...sampleImplementationState,
|
|
869
|
+
stateData: {
|
|
870
|
+
...sampleImplementationState.stateData,
|
|
871
|
+
agents: [
|
|
872
|
+
{
|
|
873
|
+
name: 'Test Agent',
|
|
874
|
+
status: 'implemented',
|
|
875
|
+
platform_artifact: 'sys_id:abc123',
|
|
876
|
+
deviations: ['Used Flow Designer instead of Workflow'],
|
|
877
|
+
integrations_connected: ['CMDB'],
|
|
878
|
+
notes: '',
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
name: 'Second Agent',
|
|
882
|
+
status: 'not_started',
|
|
883
|
+
platform_artifact: '',
|
|
884
|
+
deviations: [],
|
|
885
|
+
integrations_connected: [],
|
|
886
|
+
notes: '',
|
|
887
|
+
},
|
|
888
|
+
],
|
|
889
|
+
},
|
|
890
|
+
};
|
|
891
|
+
describe('renderSkillDirectory with no reality data', () => {
|
|
892
|
+
it('produces 13 files with null implementationState and progress', () => {
|
|
893
|
+
const input = {
|
|
894
|
+
...minimalInput,
|
|
895
|
+
implementationState: null,
|
|
896
|
+
progress: null,
|
|
897
|
+
};
|
|
898
|
+
const files = renderSkillDirectory(input);
|
|
899
|
+
expect(files.size).toBe(15);
|
|
900
|
+
expect(files.has('CURRENT-STATE.md')).toBe(false);
|
|
901
|
+
expect(files.has('RECOMMENDATIONS.md')).toBe(false);
|
|
902
|
+
});
|
|
903
|
+
it('produces 13 files with undefined implementationState', () => {
|
|
904
|
+
const input = { ...minimalInput };
|
|
905
|
+
const files = renderSkillDirectory(input);
|
|
906
|
+
expect(files.size).toBe(15);
|
|
907
|
+
});
|
|
908
|
+
it('treats all-not_started state as no data', () => {
|
|
909
|
+
const input = {
|
|
910
|
+
...minimalInput,
|
|
911
|
+
implementationState: {
|
|
912
|
+
...sampleImplementationState,
|
|
913
|
+
stateData: {
|
|
914
|
+
...sampleImplementationState.stateData,
|
|
915
|
+
agents: [{ name: 'Test Agent', status: 'not_started', platform_artifact: '', deviations: [], integrations_connected: [], notes: '' }],
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
};
|
|
919
|
+
const files = renderSkillDirectory(input);
|
|
920
|
+
expect(files.size).toBe(15);
|
|
921
|
+
expect(files.has('CURRENT-STATE.md')).toBe(false);
|
|
922
|
+
expect(files.has('RECOMMENDATIONS.md')).toBe(false);
|
|
923
|
+
});
|
|
924
|
+
it('treats empty progress actuals as no data', () => {
|
|
925
|
+
const input = {
|
|
926
|
+
...minimalInput,
|
|
927
|
+
progress: { ...sampleProgress, actuals: [], summary: { ...sampleProgress.summary, metricsRecorded: 0, onTrack: 0, minorDeviation: 0, majorDeviation: 0 } },
|
|
928
|
+
};
|
|
929
|
+
const files = renderSkillDirectory(input);
|
|
930
|
+
expect(files.size).toBe(15);
|
|
931
|
+
expect(files.has('RECOMMENDATIONS.md')).toBe(false);
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
describe('renderSkillDirectory with implementation state only', () => {
|
|
935
|
+
const stateOnlyInput = {
|
|
936
|
+
...minimalInput,
|
|
937
|
+
implementationState: sampleImplementationState,
|
|
938
|
+
};
|
|
939
|
+
it('produces 17 files (15 + CURRENT-STATE.md + RECOMMENDATIONS.md)', () => {
|
|
940
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
941
|
+
expect(files.size).toBe(17);
|
|
942
|
+
expect(files.has('CURRENT-STATE.md')).toBe(true);
|
|
943
|
+
expect(files.has('RECOMMENDATIONS.md')).toBe(true);
|
|
944
|
+
});
|
|
945
|
+
it('CURRENT-STATE.md contains agent status table', () => {
|
|
946
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
947
|
+
const state = files.get('CURRENT-STATE.md');
|
|
948
|
+
expect(state).toContain('## Agent Implementation Status');
|
|
949
|
+
expect(state).toContain('Test Agent');
|
|
950
|
+
expect(state).toContain('implemented');
|
|
951
|
+
expect(state).toContain('sys_id:abc123');
|
|
952
|
+
});
|
|
953
|
+
it('CURRENT-STATE.md contains platform comparison', () => {
|
|
954
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
955
|
+
const state = files.get('CURRENT-STATE.md');
|
|
956
|
+
expect(state).toContain('## Platform');
|
|
957
|
+
expect(state).toContain('ServiceNow');
|
|
958
|
+
expect(state).toContain('Australia');
|
|
959
|
+
});
|
|
960
|
+
it('CURRENT-STATE.md contains deviations', () => {
|
|
961
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
962
|
+
const state = files.get('CURRENT-STATE.md');
|
|
963
|
+
expect(state).toContain('Flow Designer');
|
|
964
|
+
});
|
|
965
|
+
it('CURRENT-STATE.md contains integrations', () => {
|
|
966
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
967
|
+
const state = files.get('CURRENT-STATE.md');
|
|
968
|
+
expect(state).toContain('## Integrations Connected');
|
|
969
|
+
expect(state).toContain('CMDB');
|
|
970
|
+
expect(state).toContain('Service Portal');
|
|
971
|
+
});
|
|
972
|
+
it('CURRENT-STATE.md contains additional components', () => {
|
|
973
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
974
|
+
const state = files.get('CURRENT-STATE.md');
|
|
975
|
+
expect(state).toContain('## Additional Components');
|
|
976
|
+
expect(state).toContain('Custom REST API');
|
|
977
|
+
});
|
|
978
|
+
it('CURRENT-STATE.md does not contain Performance section without progress', () => {
|
|
979
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
980
|
+
const state = files.get('CURRENT-STATE.md');
|
|
981
|
+
expect(state).not.toContain('## Performance Against Targets');
|
|
982
|
+
});
|
|
983
|
+
it('GETTING-STARTED.md is return-visit format', () => {
|
|
984
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
985
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
986
|
+
expect(guide).toContain('CONTINUING AN IMPLEMENTATION');
|
|
987
|
+
expect(guide).toContain('CURRENT-STATE.md');
|
|
988
|
+
expect(guide).toContain('RECOMMENDATIONS.md');
|
|
989
|
+
});
|
|
990
|
+
it('return-visit Step 5 has MCP tool examples', () => {
|
|
991
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
992
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
993
|
+
expect(guide).toContain('sync_implementation_state');
|
|
994
|
+
expect(guide).toContain('report_metric');
|
|
995
|
+
});
|
|
996
|
+
it('return-visit Step 5 references AGENTS.md', () => {
|
|
997
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
998
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
999
|
+
expect(guide).toContain('AGENTS.md');
|
|
1000
|
+
});
|
|
1001
|
+
it('return-visit Step 5 references hooks config', () => {
|
|
1002
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
1003
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
1004
|
+
expect(guide).toContain('hooks/claude-code-sync.json');
|
|
1005
|
+
});
|
|
1006
|
+
it('RECOMMENDATIONS.md contains deviation review', () => {
|
|
1007
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
1008
|
+
const recs = files.get('RECOMMENDATIONS.md');
|
|
1009
|
+
expect(recs).toContain('## Deviations to Review');
|
|
1010
|
+
expect(recs).toContain('Flow Designer');
|
|
1011
|
+
});
|
|
1012
|
+
it('evaluation-criteria.md is unchanged without progress', () => {
|
|
1013
|
+
const files = renderSkillDirectory(stateOnlyInput);
|
|
1014
|
+
const evalCriteria = files.get('references/evaluation-criteria.md');
|
|
1015
|
+
expect(evalCriteria).not.toContain('| Actual |');
|
|
1016
|
+
expect(evalCriteria).not.toContain('| Status |');
|
|
1017
|
+
});
|
|
1018
|
+
});
|
|
1019
|
+
describe('renderSkillDirectory with progress only', () => {
|
|
1020
|
+
const progressOnlyInput = {
|
|
1021
|
+
...minimalInput,
|
|
1022
|
+
progress: sampleProgress,
|
|
1023
|
+
};
|
|
1024
|
+
it('produces 16 files (15 + RECOMMENDATIONS.md, no CURRENT-STATE.md)', () => {
|
|
1025
|
+
const files = renderSkillDirectory(progressOnlyInput);
|
|
1026
|
+
expect(files.size).toBe(16);
|
|
1027
|
+
expect(files.has('RECOMMENDATIONS.md')).toBe(true);
|
|
1028
|
+
expect(files.has('CURRENT-STATE.md')).toBe(false);
|
|
1029
|
+
});
|
|
1030
|
+
it('GETTING-STARTED.md is first-visit format', () => {
|
|
1031
|
+
const files = renderSkillDirectory(progressOnlyInput);
|
|
1032
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
1033
|
+
expect(guide).not.toContain('CONTINUING AN IMPLEMENTATION');
|
|
1034
|
+
expect(guide).toContain('YOU ARE THE IMPLEMENTER');
|
|
1035
|
+
});
|
|
1036
|
+
it('evaluation-criteria.md is enriched with Actual columns', () => {
|
|
1037
|
+
const files = renderSkillDirectory(progressOnlyInput);
|
|
1038
|
+
const evalCriteria = files.get('references/evaluation-criteria.md');
|
|
1039
|
+
expect(evalCriteria).toContain('| Actual |');
|
|
1040
|
+
expect(evalCriteria).toContain('94%');
|
|
1041
|
+
expect(evalCriteria).toContain('minor_deviation');
|
|
1042
|
+
});
|
|
1043
|
+
it('RECOMMENDATIONS.md contains metric deviation', () => {
|
|
1044
|
+
const files = renderSkillDirectory(progressOnlyInput);
|
|
1045
|
+
const recs = files.get('RECOMMENDATIONS.md');
|
|
1046
|
+
expect(recs).toContain('## Metrics Requiring Attention');
|
|
1047
|
+
expect(recs).toContain('Test Pass Rate');
|
|
1048
|
+
expect(recs).toContain('minor_deviation');
|
|
1049
|
+
});
|
|
1050
|
+
});
|
|
1051
|
+
describe('renderSkillDirectory with full reality data', () => {
|
|
1052
|
+
const fullRealityInput = {
|
|
1053
|
+
...fullInput,
|
|
1054
|
+
implementationState: twoAgentState,
|
|
1055
|
+
progress: sampleProgress,
|
|
1056
|
+
};
|
|
1057
|
+
it('produces 17 files', () => {
|
|
1058
|
+
const files = renderSkillDirectory(fullRealityInput);
|
|
1059
|
+
expect(files.size).toBe(17);
|
|
1060
|
+
expect(files.has('CURRENT-STATE.md')).toBe(true);
|
|
1061
|
+
expect(files.has('RECOMMENDATIONS.md')).toBe(true);
|
|
1062
|
+
});
|
|
1063
|
+
it('CURRENT-STATE.md includes performance table', () => {
|
|
1064
|
+
const files = renderSkillDirectory(fullRealityInput);
|
|
1065
|
+
const state = files.get('CURRENT-STATE.md');
|
|
1066
|
+
expect(state).toContain('## Performance Against Targets');
|
|
1067
|
+
expect(state).toContain('Test Pass Rate');
|
|
1068
|
+
expect(state).toContain('94%');
|
|
1069
|
+
});
|
|
1070
|
+
it('CURRENT-STATE.md shows agent count summary', () => {
|
|
1071
|
+
const files = renderSkillDirectory(fullRealityInput);
|
|
1072
|
+
const state = files.get('CURRENT-STATE.md');
|
|
1073
|
+
expect(state).toContain('1 of 2 agents implemented');
|
|
1074
|
+
});
|
|
1075
|
+
it('RECOMMENDATIONS.md contains both agent and metric recommendations', () => {
|
|
1076
|
+
const files = renderSkillDirectory(fullRealityInput);
|
|
1077
|
+
const recs = files.get('RECOMMENDATIONS.md');
|
|
1078
|
+
expect(recs).toContain('## Next Agents to Implement');
|
|
1079
|
+
expect(recs).toContain('Second Agent');
|
|
1080
|
+
expect(recs).toContain('## Metrics Requiring Attention');
|
|
1081
|
+
expect(recs).toContain('Test Pass Rate');
|
|
1082
|
+
});
|
|
1083
|
+
it('RECOMMENDATIONS.md includes financial impact note with business case', () => {
|
|
1084
|
+
const files = renderSkillDirectory(fullRealityInput);
|
|
1085
|
+
const recs = files.get('RECOMMENDATIONS.md');
|
|
1086
|
+
expect(recs).toContain('## Financial Impact Note');
|
|
1087
|
+
expect(recs).toContain('285%');
|
|
1088
|
+
});
|
|
1089
|
+
it('GETTING-STARTED.md is return-visit format with metrics summary', () => {
|
|
1090
|
+
const files = renderSkillDirectory(fullRealityInput);
|
|
1091
|
+
const guide = files.get('GETTING-STARTED.md');
|
|
1092
|
+
expect(guide).toContain('CONTINUING AN IMPLEMENTATION');
|
|
1093
|
+
expect(guide).toContain('1 of 2 agents implemented');
|
|
1094
|
+
expect(guide).toContain('minor deviation');
|
|
1095
|
+
});
|
|
1096
|
+
it('evaluation-criteria.md is enriched with actuals', () => {
|
|
1097
|
+
const files = renderSkillDirectory(fullRealityInput);
|
|
1098
|
+
const evalCriteria = files.get('references/evaluation-criteria.md');
|
|
1099
|
+
expect(evalCriteria).toContain('| Actual |');
|
|
1100
|
+
expect(evalCriteria).toContain('94%');
|
|
1101
|
+
});
|
|
1102
|
+
});
|
|
1103
|
+
describe('RECOMMENDATIONS.md ordering', () => {
|
|
1104
|
+
it('in-progress agents show as "Continue"', () => {
|
|
1105
|
+
const input = {
|
|
1106
|
+
...minimalInput,
|
|
1107
|
+
blueprintData: {
|
|
1108
|
+
...minimalInput.blueprintData,
|
|
1109
|
+
enhancedDigitalTeam: [
|
|
1110
|
+
{ name: 'Agent A', role: 'Tester', agentRole: 'Worker' },
|
|
1111
|
+
{ name: 'Agent B', role: 'Builder', agentRole: 'Worker' },
|
|
1112
|
+
],
|
|
1113
|
+
},
|
|
1114
|
+
implementationState: {
|
|
1115
|
+
...sampleImplementationState,
|
|
1116
|
+
stateData: {
|
|
1117
|
+
...sampleImplementationState.stateData,
|
|
1118
|
+
agents: [
|
|
1119
|
+
{ name: 'Agent A', status: 'in_progress', platform_artifact: '', deviations: [], integrations_connected: [], notes: '' },
|
|
1120
|
+
{ name: 'Agent B', status: 'not_started', platform_artifact: '', deviations: [], integrations_connected: [], notes: '' },
|
|
1121
|
+
],
|
|
1122
|
+
},
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1125
|
+
const files = renderSkillDirectory(input);
|
|
1126
|
+
const recs = files.get('RECOMMENDATIONS.md');
|
|
1127
|
+
expect(recs).toContain('Continue Agent A');
|
|
1128
|
+
expect(recs).toContain('Implement Agent B');
|
|
1129
|
+
});
|
|
1130
|
+
it('major deviations before minor in metrics', () => {
|
|
1131
|
+
const input = {
|
|
1132
|
+
...minimalInput,
|
|
1133
|
+
progress: {
|
|
1134
|
+
...sampleProgress,
|
|
1135
|
+
actuals: [
|
|
1136
|
+
{ id: 'm1', metricName: 'Minor Metric', metricType: 'operational', predictedValue: '90%', actualValue: '80%', deviationPercent: -11.1, status: 'minor_deviation', recordedAt: '2026-03-18T10:00:00Z', dataSource: 'manual', recordingCount: 1 },
|
|
1137
|
+
{ id: 'm2', metricName: 'Major Metric', metricType: 'operational', predictedValue: '95%', actualValue: '60%', deviationPercent: -36.8, status: 'major_deviation', recordedAt: '2026-03-18T10:00:00Z', dataSource: 'manual', recordingCount: 1 },
|
|
1138
|
+
],
|
|
1139
|
+
summary: { totalTargets: 2, metricsRecorded: 2, onTrack: 0, minorDeviation: 1, majorDeviation: 1 },
|
|
1140
|
+
},
|
|
1141
|
+
};
|
|
1142
|
+
const files = renderSkillDirectory(input);
|
|
1143
|
+
const recs = files.get('RECOMMENDATIONS.md');
|
|
1144
|
+
const majorIdx = recs.indexOf('Major Metric');
|
|
1145
|
+
const minorIdx = recs.indexOf('Minor Metric');
|
|
1146
|
+
expect(majorIdx).toBeLessThan(minorIdx);
|
|
1147
|
+
});
|
|
1148
|
+
});
|
|
1149
|
+
describe('graceful degradation', () => {
|
|
1150
|
+
it('handles null fields in stateData without crashing', () => {
|
|
1151
|
+
const input = {
|
|
1152
|
+
...minimalInput,
|
|
1153
|
+
implementationState: {
|
|
1154
|
+
...sampleImplementationState,
|
|
1155
|
+
stateData: {
|
|
1156
|
+
overall_status: 'in_progress',
|
|
1157
|
+
agents: [{ name: 'Agent', status: 'implemented', platform_artifact: null, deviations: null, integrations_connected: null, notes: null }],
|
|
1158
|
+
platform: null,
|
|
1159
|
+
architecture: null,
|
|
1160
|
+
},
|
|
1161
|
+
},
|
|
1162
|
+
};
|
|
1163
|
+
// Should not throw
|
|
1164
|
+
const files = renderSkillDirectory(input);
|
|
1165
|
+
expect(files.has('CURRENT-STATE.md')).toBe(true);
|
|
1166
|
+
expect(files.has('RECOMMENDATIONS.md')).toBe(true);
|
|
1167
|
+
});
|
|
1168
|
+
it('all plan files still present with reality data', () => {
|
|
1169
|
+
const input = {
|
|
1170
|
+
...minimalInput,
|
|
1171
|
+
implementationState: sampleImplementationState,
|
|
1172
|
+
progress: sampleProgress,
|
|
1173
|
+
};
|
|
1174
|
+
const files = renderSkillDirectory(input);
|
|
1175
|
+
expect(files.has('SKILL.md')).toBe(true);
|
|
1176
|
+
expect(files.has('references/agent-specifications.md')).toBe(true);
|
|
1177
|
+
expect(files.has('references/financial-case.md')).toBe(true);
|
|
1178
|
+
expect(files.has('references/implementation-roadmap.md')).toBe(true);
|
|
1179
|
+
expect(files.has('GETTING-STARTED.md')).toBe(true);
|
|
1180
|
+
expect(files.has('implementation-state.yaml')).toBe(true);
|
|
1181
|
+
});
|
|
1182
|
+
it('RECOMMENDATIONS.md shows all-clear when everything is on track', () => {
|
|
1183
|
+
const input = {
|
|
1184
|
+
...minimalInput,
|
|
1185
|
+
implementationState: {
|
|
1186
|
+
...sampleImplementationState,
|
|
1187
|
+
stateData: {
|
|
1188
|
+
...sampleImplementationState.stateData,
|
|
1189
|
+
agents: [{ name: 'Test Agent', status: 'implemented', platform_artifact: 'x', deviations: [], integrations_connected: [], notes: '' }],
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
progress: {
|
|
1193
|
+
...sampleProgress,
|
|
1194
|
+
actuals: [
|
|
1195
|
+
{ id: 'm1', metricName: 'Test Pass Rate', metricType: 'operational', predictedValue: '99%', actualValue: '99.5%', status: 'on_track', recordedAt: '2026-03-18T10:00:00Z', dataSource: 'manual', recordingCount: 1 },
|
|
1196
|
+
],
|
|
1197
|
+
summary: { totalTargets: 1, metricsRecorded: 1, onTrack: 1, minorDeviation: 0, majorDeviation: 0 },
|
|
1198
|
+
},
|
|
1199
|
+
};
|
|
1200
|
+
const files = renderSkillDirectory(input);
|
|
1201
|
+
const recs = files.get('RECOMMENDATIONS.md');
|
|
1202
|
+
expect(recs).toContain('All agents implemented and metrics on track');
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
687
1205
|
//# sourceMappingURL=renderers.test.js.map
|