@zigrivers/scaffold 3.8.0 → 3.9.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 (70) hide show
  1. package/README.md +73 -8
  2. package/content/knowledge/browser-extension/browser-extension-architecture.md +195 -0
  3. package/content/knowledge/browser-extension/browser-extension-content-scripts.md +264 -0
  4. package/content/knowledge/browser-extension/browser-extension-conventions.md +156 -0
  5. package/content/knowledge/browser-extension/browser-extension-cross-browser.md +229 -0
  6. package/content/knowledge/browser-extension/browser-extension-dev-environment.md +247 -0
  7. package/content/knowledge/browser-extension/browser-extension-manifest.md +220 -0
  8. package/content/knowledge/browser-extension/browser-extension-project-structure.md +183 -0
  9. package/content/knowledge/browser-extension/browser-extension-requirements.md +107 -0
  10. package/content/knowledge/browser-extension/browser-extension-security.md +202 -0
  11. package/content/knowledge/browser-extension/browser-extension-service-workers.md +265 -0
  12. package/content/knowledge/browser-extension/browser-extension-store-submission.md +155 -0
  13. package/content/knowledge/browser-extension/browser-extension-testing.md +270 -0
  14. package/content/knowledge/data-pipeline/data-pipeline-architecture.md +175 -0
  15. package/content/knowledge/data-pipeline/data-pipeline-batch-patterns.md +263 -0
  16. package/content/knowledge/data-pipeline/data-pipeline-conventions.md +176 -0
  17. package/content/knowledge/data-pipeline/data-pipeline-dev-environment.md +350 -0
  18. package/content/knowledge/data-pipeline/data-pipeline-orchestration.md +291 -0
  19. package/content/knowledge/data-pipeline/data-pipeline-project-structure.md +257 -0
  20. package/content/knowledge/data-pipeline/data-pipeline-quality.md +324 -0
  21. package/content/knowledge/data-pipeline/data-pipeline-requirements.md +145 -0
  22. package/content/knowledge/data-pipeline/data-pipeline-schema-management.md +295 -0
  23. package/content/knowledge/data-pipeline/data-pipeline-security.md +326 -0
  24. package/content/knowledge/data-pipeline/data-pipeline-streaming-patterns.md +280 -0
  25. package/content/knowledge/data-pipeline/data-pipeline-testing.md +406 -0
  26. package/content/knowledge/ml/ml-architecture.md +172 -0
  27. package/content/knowledge/ml/ml-conventions.md +209 -0
  28. package/content/knowledge/ml/ml-dev-environment.md +299 -0
  29. package/content/knowledge/ml/ml-experiment-tracking.md +285 -0
  30. package/content/knowledge/ml/ml-model-evaluation.md +256 -0
  31. package/content/knowledge/ml/ml-observability.md +253 -0
  32. package/content/knowledge/ml/ml-project-structure.md +216 -0
  33. package/content/knowledge/ml/ml-requirements.md +138 -0
  34. package/content/knowledge/ml/ml-security.md +188 -0
  35. package/content/knowledge/ml/ml-serving-patterns.md +243 -0
  36. package/content/knowledge/ml/ml-testing.md +301 -0
  37. package/content/knowledge/ml/ml-training-patterns.md +269 -0
  38. package/content/methodology/browser-extension-overlay.yml +82 -0
  39. package/content/methodology/data-pipeline-overlay.yml +70 -0
  40. package/content/methodology/ml-overlay.yml +70 -0
  41. package/dist/cli/commands/init.d.ts +13 -0
  42. package/dist/cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/commands/init.js +122 -2
  44. package/dist/cli/commands/init.js.map +1 -1
  45. package/dist/cli/commands/init.test.js +120 -0
  46. package/dist/cli/commands/init.test.js.map +1 -1
  47. package/dist/config/schema.d.ts +864 -48
  48. package/dist/config/schema.d.ts.map +1 -1
  49. package/dist/config/schema.js +53 -0
  50. package/dist/config/schema.js.map +1 -1
  51. package/dist/config/schema.test.js +166 -3
  52. package/dist/config/schema.test.js.map +1 -1
  53. package/dist/core/assembly/overlay-loader.test.js +33 -0
  54. package/dist/core/assembly/overlay-loader.test.js.map +1 -1
  55. package/dist/e2e/project-type-overlays.test.d.ts +2 -2
  56. package/dist/e2e/project-type-overlays.test.js +499 -33
  57. package/dist/e2e/project-type-overlays.test.js.map +1 -1
  58. package/dist/types/config.d.ts +10 -1
  59. package/dist/types/config.d.ts.map +1 -1
  60. package/dist/wizard/questions.d.ts +17 -1
  61. package/dist/wizard/questions.d.ts.map +1 -1
  62. package/dist/wizard/questions.js +72 -1
  63. package/dist/wizard/questions.js.map +1 -1
  64. package/dist/wizard/questions.test.js +135 -0
  65. package/dist/wizard/questions.test.js.map +1 -1
  66. package/dist/wizard/wizard.d.ts +13 -0
  67. package/dist/wizard/wizard.d.ts.map +1 -1
  68. package/dist/wizard/wizard.js +17 -1
  69. package/dist/wizard/wizard.js.map +1 -1
  70. package/package.json +1 -1
@@ -2,8 +2,8 @@
2
2
  * E2E integration tests for project-type overlay flow:
3
3
  * init → config.yml → overlay resolution → knowledge injection
4
4
  *
5
- * Tests the full pipeline for web-app, backend, cli, library, and mobile-app
6
- * project types:
5
+ * Tests the full pipeline for web-app, backend, cli, library, mobile-app,
6
+ * data-pipeline, ml, and browser-extension project types:
7
7
  * 1. Init creates config with project-type-specific config block
8
8
  * 2. Config validates through ConfigSchema
9
9
  * 3. Overlay loads and resolves against real pipeline meta-prompts
@@ -745,48 +745,449 @@ describe('mobile-app overlay integration', () => {
745
745
  });
746
746
  });
747
747
  // ---------------------------------------------------------------------------
748
+ // Tests — Data-pipeline
749
+ // ---------------------------------------------------------------------------
750
+ describe('data-pipeline overlay integration', () => {
751
+ let tmpDir;
752
+ beforeEach(() => {
753
+ tmpDir = makeTempDir();
754
+ });
755
+ afterEach(() => {
756
+ fs.rmSync(tmpDir, { recursive: true, force: true });
757
+ vi.restoreAllMocks();
758
+ });
759
+ // Test 1: Config with data-pipeline + dataPipelineConfig validates through ConfigSchema
760
+ it('data-pipeline config with dataPipelineConfig validates through ConfigSchema', () => {
761
+ const result = ConfigSchema.safeParse({
762
+ version: 2,
763
+ methodology: 'deep',
764
+ platforms: ['claude-code'],
765
+ project: {
766
+ projectType: 'data-pipeline',
767
+ dataPipelineConfig: { processingModel: 'streaming' },
768
+ },
769
+ });
770
+ expect(result.success).toBe(true);
771
+ if (result.success) {
772
+ const project = result.data.project;
773
+ expect(project['projectType']).toBe('data-pipeline');
774
+ const dpc = project['dataPipelineConfig'];
775
+ expect(dpc['processingModel']).toBe('streaming');
776
+ expect(dpc['orchestration']).toBe('none'); // default
777
+ expect(dpc['dataQualityStrategy']).toBe('validation'); // default
778
+ expect(dpc['schemaManagement']).toBe('none'); // default
779
+ expect(dpc['hasDataCatalog']).toBe(false); // default
780
+ }
781
+ });
782
+ // Test 2: Init with projectType data-pipeline creates config with dataPipelineConfig
783
+ it('init with projectType data-pipeline creates config.yml with dataPipelineConfig defaults', async () => {
784
+ const output = createMockOutput();
785
+ const result = await runWizard({
786
+ projectRoot: tmpDir,
787
+ projectType: 'data-pipeline',
788
+ pipelineProcessing: 'batch',
789
+ methodology: 'deep',
790
+ force: false,
791
+ auto: true,
792
+ output,
793
+ });
794
+ expect(result.success).toBe(true);
795
+ const { config } = loadConfig(tmpDir, []);
796
+ expect(config).not.toBeNull();
797
+ expect(config.project?.projectType).toBe('data-pipeline');
798
+ expect(config.project?.dataPipelineConfig).toBeDefined();
799
+ expect(config.project?.dataPipelineConfig?.processingModel).toBe('batch');
800
+ });
801
+ // Test 3: config.yml round-trips through YAML correctly
802
+ it('config.yml round-trips projectType and dataPipelineConfig through YAML', async () => {
803
+ const output = createMockOutput();
804
+ await runWizard({
805
+ projectRoot: tmpDir,
806
+ projectType: 'data-pipeline',
807
+ pipelineProcessing: 'streaming',
808
+ methodology: 'deep',
809
+ force: false,
810
+ auto: true,
811
+ output,
812
+ });
813
+ const configPath = path.join(tmpDir, '.scaffold', 'config.yml');
814
+ const raw = yaml.load(fs.readFileSync(configPath, 'utf8'));
815
+ const project = raw['project'];
816
+ expect(project['projectType']).toBe('data-pipeline');
817
+ expect(project['dataPipelineConfig']).toBeDefined();
818
+ const dpc = project['dataPipelineConfig'];
819
+ expect(dpc['processingModel']).toBe('streaming');
820
+ });
821
+ // Test 4: Overlay loads successfully from content/methodology
822
+ it('data-pipeline overlay loads without errors', () => {
823
+ const methodologyDir = getPackageMethodologyDir();
824
+ const overlayPath = path.join(methodologyDir, 'data-pipeline-overlay.yml');
825
+ const { overlay, errors } = loadOverlay(overlayPath);
826
+ expect(errors).toHaveLength(0);
827
+ expect(overlay).not.toBeNull();
828
+ expect(overlay.projectType).toBe('data-pipeline');
829
+ expect(Object.keys(overlay.knowledgeOverrides).length).toBeGreaterThan(0);
830
+ });
831
+ // Test 5: Overlay injects data-pipeline knowledge into architecture step
832
+ it('overlay injects data-pipeline knowledge into system-architecture step', async () => {
833
+ const { overlayState } = await resolveProjectOverlay('data-pipeline');
834
+ expect(overlayState.knowledge['system-architecture']).toBeDefined();
835
+ expect(overlayState.knowledge['system-architecture']).toContain('data-pipeline-architecture');
836
+ expect(overlayState.knowledge['system-architecture']).toContain('data-pipeline-batch-patterns');
837
+ expect(overlayState.knowledge['system-architecture']).toContain('data-pipeline-streaming-patterns');
838
+ });
839
+ // Test 6: Overlay injects knowledge into tech-stack step
840
+ it('overlay injects data-pipeline knowledge into tech-stack step', async () => {
841
+ const { overlayState } = await resolveProjectOverlay('data-pipeline');
842
+ expect(overlayState.knowledge['tech-stack']).toBeDefined();
843
+ expect(overlayState.knowledge['tech-stack']).toContain('data-pipeline-architecture');
844
+ });
845
+ // Test 7: Overlay injects knowledge into testing steps
846
+ it('overlay injects data-pipeline knowledge into TDD, e2e, and create-evals steps', async () => {
847
+ const { overlayState } = await resolveProjectOverlay('data-pipeline');
848
+ expect(overlayState.knowledge['tdd']).toBeDefined();
849
+ expect(overlayState.knowledge['tdd']).toContain('data-pipeline-testing');
850
+ expect(overlayState.knowledge['tdd']).toContain('data-pipeline-quality');
851
+ expect(overlayState.knowledge['add-e2e-testing']).toBeDefined();
852
+ expect(overlayState.knowledge['add-e2e-testing']).toContain('data-pipeline-testing');
853
+ expect(overlayState.knowledge['create-evals']).toBeDefined();
854
+ expect(overlayState.knowledge['create-evals']).toContain('data-pipeline-testing');
855
+ expect(overlayState.knowledge['create-evals']).toContain('data-pipeline-quality');
856
+ });
857
+ // Test 8: Overlay injects knowledge into foundational steps
858
+ it('overlay injects data-pipeline knowledge into foundational steps', async () => {
859
+ const { overlayState } = await resolveProjectOverlay('data-pipeline');
860
+ expect(overlayState.knowledge['create-prd']).toContain('data-pipeline-requirements');
861
+ expect(overlayState.knowledge['coding-standards']).toContain('data-pipeline-conventions');
862
+ expect(overlayState.knowledge['project-structure']).toContain('data-pipeline-project-structure');
863
+ });
864
+ // Test 9: Overlay injects knowledge into domain-modeling, security, and operations steps
865
+ it('overlay injects data-pipeline knowledge into domain-modeling, security, and operations', async () => {
866
+ const { overlayState } = await resolveProjectOverlay('data-pipeline');
867
+ expect(overlayState.knowledge['domain-modeling']).toBeDefined();
868
+ expect(overlayState.knowledge['domain-modeling']).toContain('data-pipeline-schema-management');
869
+ expect(overlayState.knowledge['security']).toBeDefined();
870
+ expect(overlayState.knowledge['security']).toContain('data-pipeline-security');
871
+ expect(overlayState.knowledge['operations']).toBeDefined();
872
+ expect(overlayState.knowledge['operations']).toContain('data-pipeline-orchestration');
873
+ });
874
+ // Test 10: MVP methodology with data-pipeline overlay works
875
+ it('MVP methodology with data-pipeline overlay injects knowledge', async () => {
876
+ const { overlayState } = await resolveProjectOverlay('data-pipeline', 'mvp');
877
+ expect(overlayState.knowledge['system-architecture']).toContain('data-pipeline-architecture');
878
+ expect(overlayState.knowledge['tdd']).toContain('data-pipeline-testing');
879
+ });
880
+ });
881
+ // ---------------------------------------------------------------------------
882
+ // Tests — ML
883
+ // ---------------------------------------------------------------------------
884
+ describe('ml overlay integration', () => {
885
+ let tmpDir;
886
+ beforeEach(() => {
887
+ tmpDir = makeTempDir();
888
+ });
889
+ afterEach(() => {
890
+ fs.rmSync(tmpDir, { recursive: true, force: true });
891
+ vi.restoreAllMocks();
892
+ });
893
+ // Test 1: Config with ml + mlConfig validates through ConfigSchema
894
+ it('ml config with mlConfig validates through ConfigSchema', () => {
895
+ const result = ConfigSchema.safeParse({
896
+ version: 2,
897
+ methodology: 'deep',
898
+ platforms: ['claude-code'],
899
+ project: {
900
+ projectType: 'ml',
901
+ mlConfig: { projectPhase: 'training' },
902
+ },
903
+ });
904
+ expect(result.success).toBe(true);
905
+ if (result.success) {
906
+ const project = result.data.project;
907
+ expect(project['projectType']).toBe('ml');
908
+ const mc = project['mlConfig'];
909
+ expect(mc['projectPhase']).toBe('training');
910
+ expect(mc['modelType']).toBe('deep-learning'); // default
911
+ expect(mc['servingPattern']).toBe('none'); // default
912
+ expect(mc['hasExperimentTracking']).toBe(true); // default
913
+ }
914
+ });
915
+ // Test 2: Init with projectType ml creates config with mlConfig
916
+ it('init with projectType ml creates config.yml with mlConfig defaults', async () => {
917
+ const output = createMockOutput();
918
+ const result = await runWizard({
919
+ projectRoot: tmpDir,
920
+ projectType: 'ml',
921
+ mlPhase: 'training',
922
+ methodology: 'deep',
923
+ force: false,
924
+ auto: true,
925
+ output,
926
+ });
927
+ expect(result.success).toBe(true);
928
+ const { config } = loadConfig(tmpDir, []);
929
+ expect(config).not.toBeNull();
930
+ expect(config.project?.projectType).toBe('ml');
931
+ expect(config.project?.mlConfig).toBeDefined();
932
+ expect(config.project?.mlConfig?.projectPhase).toBe('training');
933
+ });
934
+ // Test 3: config.yml round-trips through YAML correctly
935
+ it('config.yml round-trips projectType and mlConfig through YAML', async () => {
936
+ const output = createMockOutput();
937
+ await runWizard({
938
+ projectRoot: tmpDir,
939
+ projectType: 'ml',
940
+ mlPhase: 'inference',
941
+ mlServing: 'realtime',
942
+ methodology: 'deep',
943
+ force: false,
944
+ auto: true,
945
+ output,
946
+ });
947
+ const configPath = path.join(tmpDir, '.scaffold', 'config.yml');
948
+ const raw = yaml.load(fs.readFileSync(configPath, 'utf8'));
949
+ const project = raw['project'];
950
+ expect(project['projectType']).toBe('ml');
951
+ expect(project['mlConfig']).toBeDefined();
952
+ const mc = project['mlConfig'];
953
+ expect(mc['projectPhase']).toBe('inference');
954
+ expect(mc['servingPattern']).toBe('realtime');
955
+ });
956
+ // Test 4: Overlay loads successfully from content/methodology
957
+ it('ml overlay loads without errors', () => {
958
+ const methodologyDir = getPackageMethodologyDir();
959
+ const overlayPath = path.join(methodologyDir, 'ml-overlay.yml');
960
+ const { overlay, errors } = loadOverlay(overlayPath);
961
+ expect(errors).toHaveLength(0);
962
+ expect(overlay).not.toBeNull();
963
+ expect(overlay.projectType).toBe('ml');
964
+ expect(Object.keys(overlay.knowledgeOverrides).length).toBeGreaterThan(0);
965
+ });
966
+ // Test 5: Overlay injects ml knowledge into architecture step
967
+ it('overlay injects ml knowledge into system-architecture step', async () => {
968
+ const { overlayState } = await resolveProjectOverlay('ml');
969
+ expect(overlayState.knowledge['system-architecture']).toBeDefined();
970
+ expect(overlayState.knowledge['system-architecture']).toContain('ml-architecture');
971
+ expect(overlayState.knowledge['system-architecture']).toContain('ml-training-patterns');
972
+ expect(overlayState.knowledge['system-architecture']).toContain('ml-serving-patterns');
973
+ });
974
+ // Test 6: Overlay injects knowledge into tech-stack step
975
+ it('overlay injects ml knowledge into tech-stack step', async () => {
976
+ const { overlayState } = await resolveProjectOverlay('ml');
977
+ expect(overlayState.knowledge['tech-stack']).toBeDefined();
978
+ expect(overlayState.knowledge['tech-stack']).toContain('ml-architecture');
979
+ });
980
+ // Test 7: Overlay injects knowledge into testing steps
981
+ it('overlay injects ml knowledge into TDD, e2e, and create-evals steps', async () => {
982
+ const { overlayState } = await resolveProjectOverlay('ml');
983
+ expect(overlayState.knowledge['tdd']).toBeDefined();
984
+ expect(overlayState.knowledge['tdd']).toContain('ml-testing');
985
+ expect(overlayState.knowledge['add-e2e-testing']).toBeDefined();
986
+ expect(overlayState.knowledge['add-e2e-testing']).toContain('ml-testing');
987
+ // model-evaluation routes to create-evals
988
+ expect(overlayState.knowledge['create-evals']).toBeDefined();
989
+ expect(overlayState.knowledge['create-evals']).toContain('ml-testing');
990
+ expect(overlayState.knowledge['create-evals']).toContain('ml-model-evaluation');
991
+ });
992
+ // Test 8: Overlay injects knowledge into foundational steps
993
+ it('overlay injects ml knowledge into foundational steps', async () => {
994
+ const { overlayState } = await resolveProjectOverlay('ml');
995
+ expect(overlayState.knowledge['create-prd']).toContain('ml-requirements');
996
+ expect(overlayState.knowledge['coding-standards']).toContain('ml-conventions');
997
+ expect(overlayState.knowledge['project-structure']).toContain('ml-project-structure');
998
+ });
999
+ // Test 9: Overlay injects knowledge into security and operations (experiment tracking + observability)
1000
+ it('overlay injects ml knowledge into security and operations steps', async () => {
1001
+ const { overlayState } = await resolveProjectOverlay('ml');
1002
+ expect(overlayState.knowledge['security']).toBeDefined();
1003
+ expect(overlayState.knowledge['security']).toContain('ml-security');
1004
+ expect(overlayState.knowledge['operations']).toBeDefined();
1005
+ expect(overlayState.knowledge['operations']).toContain('ml-experiment-tracking');
1006
+ expect(overlayState.knowledge['operations']).toContain('ml-observability');
1007
+ });
1008
+ // Test 10: MVP methodology with ml overlay works
1009
+ it('MVP methodology with ml overlay injects knowledge', async () => {
1010
+ const { overlayState } = await resolveProjectOverlay('ml', 'mvp');
1011
+ expect(overlayState.knowledge['system-architecture']).toContain('ml-architecture');
1012
+ expect(overlayState.knowledge['create-evals']).toContain('ml-model-evaluation');
1013
+ });
1014
+ });
1015
+ // ---------------------------------------------------------------------------
1016
+ // Tests — Browser-extension
1017
+ // ---------------------------------------------------------------------------
1018
+ describe('browser-extension overlay integration', () => {
1019
+ let tmpDir;
1020
+ beforeEach(() => {
1021
+ tmpDir = makeTempDir();
1022
+ });
1023
+ afterEach(() => {
1024
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1025
+ vi.restoreAllMocks();
1026
+ });
1027
+ // Test 1: Config with browser-extension + browserExtensionConfig validates through ConfigSchema
1028
+ it('browser-extension config with browserExtensionConfig validates through ConfigSchema', () => {
1029
+ const result = ConfigSchema.safeParse({
1030
+ version: 2,
1031
+ methodology: 'deep',
1032
+ platforms: ['claude-code'],
1033
+ project: {
1034
+ projectType: 'browser-extension',
1035
+ browserExtensionConfig: { manifestVersion: '3' },
1036
+ },
1037
+ });
1038
+ expect(result.success).toBe(true);
1039
+ if (result.success) {
1040
+ const project = result.data.project;
1041
+ expect(project['projectType']).toBe('browser-extension');
1042
+ const bec = project['browserExtensionConfig'];
1043
+ expect(bec['manifestVersion']).toBe('3');
1044
+ expect(bec['uiSurfaces']).toEqual(['popup']); // default
1045
+ expect(bec['hasContentScript']).toBe(false); // default
1046
+ expect(bec['hasBackgroundWorker']).toBe(true); // default
1047
+ }
1048
+ });
1049
+ // Test 2: Init with projectType browser-extension creates config (no required flag in auto mode)
1050
+ it('init with projectType browser-extension creates config.yml with browserExtensionConfig defaults', async () => {
1051
+ const output = createMockOutput();
1052
+ const result = await runWizard({
1053
+ projectRoot: tmpDir,
1054
+ projectType: 'browser-extension',
1055
+ methodology: 'deep',
1056
+ force: false,
1057
+ auto: true,
1058
+ output,
1059
+ });
1060
+ expect(result.success).toBe(true);
1061
+ const { config } = loadConfig(tmpDir, []);
1062
+ expect(config).not.toBeNull();
1063
+ expect(config.project?.projectType).toBe('browser-extension');
1064
+ expect(config.project?.browserExtensionConfig).toBeDefined();
1065
+ expect(config.project?.browserExtensionConfig?.manifestVersion).toBe('3');
1066
+ expect(config.project?.browserExtensionConfig?.hasBackgroundWorker).toBe(true);
1067
+ });
1068
+ // Test 3: config.yml round-trips through YAML correctly
1069
+ it('config.yml round-trips projectType and browserExtensionConfig through YAML', async () => {
1070
+ const output = createMockOutput();
1071
+ await runWizard({
1072
+ projectRoot: tmpDir,
1073
+ projectType: 'browser-extension',
1074
+ extManifest: '3',
1075
+ extUiSurfaces: ['popup', 'sidepanel'],
1076
+ extContentScript: true,
1077
+ methodology: 'deep',
1078
+ force: false,
1079
+ auto: true,
1080
+ output,
1081
+ });
1082
+ const configPath = path.join(tmpDir, '.scaffold', 'config.yml');
1083
+ const raw = yaml.load(fs.readFileSync(configPath, 'utf8'));
1084
+ const project = raw['project'];
1085
+ expect(project['projectType']).toBe('browser-extension');
1086
+ expect(project['browserExtensionConfig']).toBeDefined();
1087
+ const bec = project['browserExtensionConfig'];
1088
+ expect(bec['manifestVersion']).toBe('3');
1089
+ expect(bec['uiSurfaces']).toEqual(['popup', 'sidepanel']);
1090
+ expect(bec['hasContentScript']).toBe(true);
1091
+ });
1092
+ // Test 4: Overlay loads successfully from content/methodology
1093
+ it('browser-extension overlay loads without errors', () => {
1094
+ const methodologyDir = getPackageMethodologyDir();
1095
+ const overlayPath = path.join(methodologyDir, 'browser-extension-overlay.yml');
1096
+ const { overlay, errors } = loadOverlay(overlayPath);
1097
+ expect(errors).toHaveLength(0);
1098
+ expect(overlay).not.toBeNull();
1099
+ expect(overlay.projectType).toBe('browser-extension');
1100
+ expect(Object.keys(overlay.knowledgeOverrides).length).toBeGreaterThan(0);
1101
+ });
1102
+ // Test 5: Overlay injects browser-extension knowledge into architecture step
1103
+ it('overlay injects browser-extension knowledge into system-architecture step', async () => {
1104
+ const { overlayState } = await resolveProjectOverlay('browser-extension');
1105
+ expect(overlayState.knowledge['system-architecture']).toBeDefined();
1106
+ expect(overlayState.knowledge['system-architecture']).toContain('browser-extension-architecture');
1107
+ expect(overlayState.knowledge['system-architecture']).toContain('browser-extension-service-workers');
1108
+ });
1109
+ // Test 6: Overlay injects knowledge into tech-stack step
1110
+ it('overlay injects browser-extension knowledge into tech-stack step', async () => {
1111
+ const { overlayState } = await resolveProjectOverlay('browser-extension');
1112
+ expect(overlayState.knowledge['tech-stack']).toBeDefined();
1113
+ expect(overlayState.knowledge['tech-stack']).toContain('browser-extension-architecture');
1114
+ expect(overlayState.knowledge['tech-stack']).toContain('browser-extension-manifest');
1115
+ });
1116
+ // Test 7: Overlay injects knowledge into testing steps
1117
+ it('overlay injects browser-extension knowledge into TDD and e2e steps (cross-browser)', async () => {
1118
+ const { overlayState } = await resolveProjectOverlay('browser-extension');
1119
+ expect(overlayState.knowledge['tdd']).toBeDefined();
1120
+ expect(overlayState.knowledge['tdd']).toContain('browser-extension-testing');
1121
+ expect(overlayState.knowledge['tdd']).toContain('browser-extension-cross-browser');
1122
+ expect(overlayState.knowledge['add-e2e-testing']).toBeDefined();
1123
+ expect(overlayState.knowledge['add-e2e-testing']).toContain('browser-extension-testing');
1124
+ expect(overlayState.knowledge['add-e2e-testing']).toContain('browser-extension-cross-browser');
1125
+ });
1126
+ // Test 8: Overlay injects knowledge into foundational steps (manifest in coding-standards)
1127
+ it('overlay injects browser-extension knowledge into foundational steps', async () => {
1128
+ const { overlayState } = await resolveProjectOverlay('browser-extension');
1129
+ expect(overlayState.knowledge['create-prd']).toContain('browser-extension-requirements');
1130
+ expect(overlayState.knowledge['coding-standards']).toContain('browser-extension-conventions');
1131
+ expect(overlayState.knowledge['coding-standards']).toContain('browser-extension-manifest');
1132
+ expect(overlayState.knowledge['project-structure']).toContain('browser-extension-project-structure');
1133
+ });
1134
+ // Test 9: Overlay injects knowledge into security, ux-spec, and operations steps
1135
+ it('overlay injects browser-extension knowledge into security, ux-spec, and operations', async () => {
1136
+ const { overlayState } = await resolveProjectOverlay('browser-extension');
1137
+ expect(overlayState.knowledge['security']).toBeDefined();
1138
+ expect(overlayState.knowledge['security']).toContain('browser-extension-security');
1139
+ expect(overlayState.knowledge['security']).toContain('browser-extension-content-scripts');
1140
+ expect(overlayState.knowledge['ux-spec']).toBeDefined();
1141
+ expect(overlayState.knowledge['ux-spec']).toContain('browser-extension-architecture');
1142
+ expect(overlayState.knowledge['operations']).toBeDefined();
1143
+ expect(overlayState.knowledge['operations']).toContain('browser-extension-store-submission');
1144
+ });
1145
+ // Test 10: MVP methodology with browser-extension overlay works
1146
+ it('MVP methodology with browser-extension overlay injects knowledge', async () => {
1147
+ const { overlayState } = await resolveProjectOverlay('browser-extension', 'mvp');
1148
+ expect(overlayState.knowledge['system-architecture']).toContain('browser-extension-architecture');
1149
+ expect(overlayState.knowledge['tdd']).toContain('browser-extension-cross-browser');
1150
+ });
1151
+ });
1152
+ // ---------------------------------------------------------------------------
748
1153
  // Cross-type validation tests
749
1154
  // ---------------------------------------------------------------------------
750
1155
  describe('project-type overlay cross-validation', () => {
751
1156
  it('each overlay type injects distinct knowledge entries (no accidental overlap)', async () => {
752
- const [webResult, backendResult, cliResult, libraryResult, mobileResult] = await Promise.all([
1157
+ const [webResult, backendResult, cliResult, libraryResult, mobileResult, pipelineResult, mlResult, extResult,] = await Promise.all([
753
1158
  resolveProjectOverlay('web-app'),
754
1159
  resolveProjectOverlay('backend'),
755
1160
  resolveProjectOverlay('cli'),
756
1161
  resolveProjectOverlay('library'),
757
1162
  resolveProjectOverlay('mobile-app'),
1163
+ resolveProjectOverlay('data-pipeline'),
1164
+ resolveProjectOverlay('ml'),
1165
+ resolveProjectOverlay('browser-extension'),
758
1166
  ]);
759
1167
  // system-architecture should have type-specific entries for each
760
- const webArch = webResult.overlayState.knowledge['system-architecture'] ?? [];
761
- const backendArch = backendResult.overlayState.knowledge['system-architecture'] ?? [];
762
- const cliArch = cliResult.overlayState.knowledge['system-architecture'] ?? [];
763
- const libraryArch = libraryResult.overlayState.knowledge['system-architecture'] ?? [];
764
- const mobileArch = mobileResult.overlayState.knowledge['system-architecture'] ?? [];
765
- expect(webArch).toContain('web-app-architecture');
766
- expect(webArch).not.toContain('backend-architecture');
767
- expect(webArch).not.toContain('cli-architecture');
768
- expect(webArch).not.toContain('library-architecture');
769
- expect(webArch).not.toContain('mobile-app-architecture');
770
- expect(backendArch).toContain('backend-architecture');
771
- expect(backendArch).not.toContain('web-app-architecture');
772
- expect(backendArch).not.toContain('cli-architecture');
773
- expect(backendArch).not.toContain('library-architecture');
774
- expect(backendArch).not.toContain('mobile-app-architecture');
775
- expect(cliArch).toContain('cli-architecture');
776
- expect(cliArch).not.toContain('web-app-architecture');
777
- expect(cliArch).not.toContain('backend-architecture');
778
- expect(cliArch).not.toContain('library-architecture');
779
- expect(cliArch).not.toContain('mobile-app-architecture');
780
- expect(libraryArch).toContain('library-architecture');
781
- expect(libraryArch).not.toContain('web-app-architecture');
782
- expect(libraryArch).not.toContain('backend-architecture');
783
- expect(libraryArch).not.toContain('cli-architecture');
784
- expect(libraryArch).not.toContain('mobile-app-architecture');
785
- expect(mobileArch).toContain('mobile-app-architecture');
786
- expect(mobileArch).not.toContain('web-app-architecture');
787
- expect(mobileArch).not.toContain('backend-architecture');
788
- expect(mobileArch).not.toContain('cli-architecture');
789
- expect(mobileArch).not.toContain('library-architecture');
1168
+ const allArch = {
1169
+ 'web-app': webResult.overlayState.knowledge['system-architecture'] ?? [],
1170
+ 'backend': backendResult.overlayState.knowledge['system-architecture'] ?? [],
1171
+ 'cli': cliResult.overlayState.knowledge['system-architecture'] ?? [],
1172
+ 'library': libraryResult.overlayState.knowledge['system-architecture'] ?? [],
1173
+ 'mobile-app': mobileResult.overlayState.knowledge['system-architecture'] ?? [],
1174
+ 'data-pipeline': pipelineResult.overlayState.knowledge['system-architecture'] ?? [],
1175
+ 'ml': mlResult.overlayState.knowledge['system-architecture'] ?? [],
1176
+ 'browser-extension': extResult.overlayState.knowledge['system-architecture'] ?? [],
1177
+ };
1178
+ // Each project type's architecture step contains its own -architecture entry
1179
+ // and none of the other 7 type-specific architecture entries.
1180
+ const allTypes = Object.keys(allArch);
1181
+ for (const type of allTypes) {
1182
+ const ownEntry = `${type}-architecture`;
1183
+ expect(allArch[type], `${type} should contain ${ownEntry}`).toContain(ownEntry);
1184
+ for (const otherType of allTypes) {
1185
+ if (otherType === type)
1186
+ continue;
1187
+ const otherEntry = `${otherType}-architecture`;
1188
+ expect(allArch[type], `${type} system-architecture must not leak ${otherEntry}`).not.toContain(otherEntry);
1189
+ }
1190
+ }
790
1191
  });
791
1192
  it('config schema rejects cross-typed config blocks', () => {
792
1193
  // gameConfig on web-app
@@ -829,6 +1230,71 @@ describe('project-type overlay cross-validation', () => {
829
1230
  version: 2, methodology: 'deep', platforms: ['claude-code'],
830
1231
  project: { projectType: 'library', mobileAppConfig: { platform: 'android' } },
831
1232
  }).success).toBe(false);
1233
+ // dataPipelineConfig on backend
1234
+ expect(ConfigSchema.safeParse({
1235
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1236
+ project: { projectType: 'backend', dataPipelineConfig: { processingModel: 'streaming' } },
1237
+ }).success).toBe(false);
1238
+ // mlConfig on data-pipeline
1239
+ expect(ConfigSchema.safeParse({
1240
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1241
+ project: { projectType: 'data-pipeline', mlConfig: { projectPhase: 'training' } },
1242
+ }).success).toBe(false);
1243
+ // browserExtensionConfig on web-app
1244
+ expect(ConfigSchema.safeParse({
1245
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1246
+ project: { projectType: 'web-app', browserExtensionConfig: { manifestVersion: '3' } },
1247
+ }).success).toBe(false);
1248
+ // dataPipelineConfig on ml
1249
+ expect(ConfigSchema.safeParse({
1250
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1251
+ project: { projectType: 'ml', dataPipelineConfig: { processingModel: 'batch' } },
1252
+ }).success).toBe(false);
1253
+ });
1254
+ it('ml inference projects must specify a serving pattern', () => {
1255
+ // inference + none → invalid
1256
+ expect(ConfigSchema.safeParse({
1257
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1258
+ project: { projectType: 'ml', mlConfig: { projectPhase: 'inference', servingPattern: 'none' } },
1259
+ }).success).toBe(false);
1260
+ // inference + realtime → valid
1261
+ expect(ConfigSchema.safeParse({
1262
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1263
+ project: { projectType: 'ml', mlConfig: { projectPhase: 'inference', servingPattern: 'realtime' } },
1264
+ }).success).toBe(true);
1265
+ // training + serving pattern → invalid
1266
+ expect(ConfigSchema.safeParse({
1267
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1268
+ project: { projectType: 'ml', mlConfig: { projectPhase: 'training', servingPattern: 'realtime' } },
1269
+ }).success).toBe(false);
1270
+ });
1271
+ it('browser-extension must have at least one capability', () => {
1272
+ // No surfaces, no content script, no background worker → invalid
1273
+ expect(ConfigSchema.safeParse({
1274
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1275
+ project: {
1276
+ projectType: 'browser-extension',
1277
+ browserExtensionConfig: {
1278
+ manifestVersion: '3',
1279
+ uiSurfaces: [],
1280
+ hasContentScript: false,
1281
+ hasBackgroundWorker: false,
1282
+ },
1283
+ },
1284
+ }).success).toBe(false);
1285
+ // Just a content script → valid
1286
+ expect(ConfigSchema.safeParse({
1287
+ version: 2, methodology: 'deep', platforms: ['claude-code'],
1288
+ project: {
1289
+ projectType: 'browser-extension',
1290
+ browserExtensionConfig: {
1291
+ manifestVersion: '3',
1292
+ uiSurfaces: [],
1293
+ hasContentScript: true,
1294
+ hasBackgroundWorker: false,
1295
+ },
1296
+ },
1297
+ }).success).toBe(true);
832
1298
  });
833
1299
  });
834
1300
  //# sourceMappingURL=project-type-overlays.test.js.map