jbom 4.5.1__tar.gz → 4.7.0__tar.gz

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 (144) hide show
  1. {jbom-4.5.1/src/jbom.egg-info → jbom-4.7.0}/PKG-INFO +1 -1
  2. jbom-4.7.0/docs/design/workflow-architecture.md +693 -0
  3. {jbom-4.5.1 → jbom-4.7.0}/pyproject.toml +24 -2
  4. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/__version__.py +1 -1
  5. {jbom-4.5.1 → jbom-4.7.0/src/jbom.egg-info}/PKG-INFO +1 -1
  6. {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/SOURCES.txt +1 -1
  7. jbom-4.5.1/docs/design/plugin-architecture-proposal.md +0 -432
  8. {jbom-4.5.1 → jbom-4.7.0}/LICENSE +0 -0
  9. {jbom-4.5.1 → jbom-4.7.0}/MANIFEST.in +0 -0
  10. {jbom-4.5.1 → jbom-4.7.0}/README.md +0 -0
  11. {jbom-4.5.1 → jbom-4.7.0}/docs/CHANGELOG.md +0 -0
  12. {jbom-4.5.1 → jbom-4.7.0}/docs/CONTRIBUTING.md +0 -0
  13. {jbom-4.5.1 → jbom-4.7.0}/docs/README.configuration.md +0 -0
  14. {jbom-4.5.1 → jbom-4.7.0}/docs/README.developer.md +0 -0
  15. {jbom-4.5.1 → jbom-4.7.0}/docs/README.man1.md +0 -0
  16. {jbom-4.5.1 → jbom-4.7.0}/docs/README.man3.md +0 -0
  17. {jbom-4.5.1 → jbom-4.7.0}/docs/README.man4.md +0 -0
  18. {jbom-4.5.1 → jbom-4.7.0}/docs/README.man5.md +0 -0
  19. {jbom-4.5.1 → jbom-4.7.0}/docs/README.tests.md +0 -0
  20. {jbom-4.5.1 → jbom-4.7.0}/docs/WARP.md +0 -0
  21. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/BDD_AXIOMS.md +0 -0
  22. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/BEHAVE_SUBDIRECTORY_LOADING.md +0 -0
  23. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/PROJECT_INPUT_RESOLUTION_TESTS.feature +0 -0
  24. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/README.md +0 -0
  25. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/back_annotation_requirements.md +0 -0
  26. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/component_rotation_correction_requirements.md +0 -0
  27. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/comprehensive_fault_testing_requirements.md +0 -0
  28. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/fabrication_platform_requirements.md +0 -0
  29. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/fabricator_integration_requirements.md +0 -0
  30. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/completed/federated_inventory_requirements.md +0 -0
  31. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/completed/inventory_management_requirements.md +0 -0
  32. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/development_tasks.md +0 -0
  33. {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/sample_detailed_validation_report.txt +0 -0
  34. {jbom-4.5.1 → jbom-4.7.0}/docs/jbom-config-schema.yaml +0 -0
  35. {jbom-4.5.1 → jbom-4.7.0}/docs/test_diagnostics_phase1-2_complete.md +0 -0
  36. {jbom-4.5.1 → jbom-4.7.0}/docs/test_failure_diagnostics_proposal.md +0 -0
  37. {jbom-4.5.1 → jbom-4.7.0}/kicad_jbom_plugin.py +0 -0
  38. {jbom-4.5.1 → jbom-4.7.0}/setup.cfg +0 -0
  39. {jbom-4.5.1 → jbom-4.7.0}/setup.py +0 -0
  40. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/__init__.py +0 -0
  41. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/__main__.py +0 -0
  42. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/api.py +0 -0
  43. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/__init__.py +0 -0
  44. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/base.py +0 -0
  45. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/__init__.py +0 -0
  46. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/annotate.py +0 -0
  47. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/bom.py +0 -0
  48. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/inventory.py +0 -0
  49. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/inventory_search.py +0 -0
  50. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/pos.py +0 -0
  51. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/search.py +0 -0
  52. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/common.py +0 -0
  53. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/formatting.py +0 -0
  54. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/main.py +0 -0
  55. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/main_old.py +0 -0
  56. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/__init__.py +0 -0
  57. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/config.py +0 -0
  58. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/config_fabricators.py +0 -0
  59. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/constants.py +0 -0
  60. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/fabricators.py +0 -0
  61. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/fields.py +0 -0
  62. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/fields_system.py +0 -0
  63. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/generator.py +0 -0
  64. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/options.py +0 -0
  65. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/output.py +0 -0
  66. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/packages.py +0 -0
  67. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/sexp_parser.py +0 -0
  68. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/types.py +0 -0
  69. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/utils.py +0 -0
  70. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/values.py +0 -0
  71. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/defaults.yaml +0 -0
  72. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/generic.fab.yaml +0 -0
  73. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/jlc.fab.yaml +0 -0
  74. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/pcbway.fab.yaml +0 -0
  75. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/seeed.fab.yaml +0 -0
  76. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/generators/__init__.py +0 -0
  77. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/generators/bom.py +0 -0
  78. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/generators/pos.py +0 -0
  79. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/__init__.py +0 -0
  80. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/inventory.py +0 -0
  81. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/jlc_loader.py +0 -0
  82. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/pcb.py +0 -0
  83. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/pcb_model.py +0 -0
  84. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/project_inventory.py +0 -0
  85. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/schematic.py +0 -0
  86. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/__init__.py +0 -0
  87. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/annotator.py +0 -0
  88. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/classifier.py +0 -0
  89. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/component_types.py +0 -0
  90. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/inventory_enricher.py +0 -0
  91. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/inventory_matcher.py +0 -0
  92. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/search_result_scorer.py +0 -0
  93. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/sch_api/model.py +0 -0
  94. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/search/__init__.py +0 -0
  95. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/search/filter.py +0 -0
  96. {jbom-4.5.1 → jbom-4.7.0}/src/jbom/search/mouser.py +0 -0
  97. {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/dependency_links.txt +0 -0
  98. {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/entry_points.txt +0 -0
  99. {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/requires.txt +0 -0
  100. {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/top_level.txt +0 -0
  101. {jbom-4.5.1 → jbom-4.7.0}/tests/__init__.py +0 -0
  102. {jbom-4.5.1 → jbom-4.7.0}/tests/config/__init__.py +0 -0
  103. {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_config_cli_integration.py +0 -0
  104. {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_config_system.py +0 -0
  105. {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_config_units.py +0 -0
  106. {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_hierarchical_config.py +0 -0
  107. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/__init__.py +0 -0
  108. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_back_annotation.py +0 -0
  109. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_base.py +0 -0
  110. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_bom.py +0 -0
  111. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_bom_errors.py +0 -0
  112. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_classification.py +0 -0
  113. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_fabrication.py +0 -0
  114. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_fabricator.py +0 -0
  115. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_federation.py +0 -0
  116. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_inventory_formats.py +0 -0
  117. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_inventory_generation.py +0 -0
  118. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_pos.py +0 -0
  119. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_pos_config.py +0 -0
  120. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_pos_errors.py +0 -0
  121. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_schematic_edge_cases.py +0 -0
  122. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_search.py +0 -0
  123. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_integration_all_interfaces.py +0 -0
  124. {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_kicad_plugin.py +0 -0
  125. {jbom-4.5.1 → jbom-4.7.0}/tests/test_integration_projects.py +0 -0
  126. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/__init__.py +0 -0
  127. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_api_enriched_inventory.py +0 -0
  128. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_api_v3.py +0 -0
  129. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_classifier.py +0 -0
  130. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_cli.py +0 -0
  131. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_cli_inventory_command.py +0 -0
  132. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_common_fields.py +0 -0
  133. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_federated_inventory.py +0 -0
  134. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_generators_bom.py +0 -0
  135. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_generic_fabricator.py +0 -0
  136. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_inventory_enricher.py +0 -0
  137. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_inventory_matching.py +0 -0
  138. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_inventory_numbers_real.py +0 -0
  139. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_loaders_schematic.py +0 -0
  140. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_pcbway_poc.py +0 -0
  141. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_position.py +0 -0
  142. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_processors_value_parsing.py +0 -0
  143. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_project_inventory.py +0 -0
  144. {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_search_result_scorer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jbom
3
- Version: 4.5.1
3
+ Version: 4.7.0
4
4
  Summary: Intelligent KiCad Bill of Materials generator with inventory matching
5
5
  Author-email: John Plocher <John.Plocher@gmail.com>
6
6
  License-Expression: AGPL-3.0-only
@@ -0,0 +1,693 @@
1
+ # jBOM Workflow Architecture
2
+
3
+ ## Domain Context
4
+
5
+ **jBOM exists to transform KiCad projects into fabrication outputs.** Users have KiCad schematics/PCBs and need BOMs, placement files, inventory reports, etc. for manufacturing.
6
+
7
+ The workflow pattern:
8
+ 1. **Ingest** - Read KiCad projects, inventory spreadsheets
9
+ 2. **Process** - Match components, group, filter, enrich with data
10
+ 3. **Output** - Generate fabricator-specific files (CSV, reports)
11
+
12
+ ## Core Architectural Concept
13
+
14
+ **Plugins provide reusable service modules (APIs).**
15
+ **Workflows compose those services into named commands.**
16
+
17
+ ```
18
+ Plugin Modules (Services) Workflows (Command Compositions)
19
+ ├─ KiCadReader ├─ bom: compose(readProject, createBOM, print)
20
+ ├─ InventoryService ├─ pos: compose(readPCB, extractPlacement, print)
21
+ ├─ BOMGenerator └─ inventory: compose(scanProject, buildInventory, print)
22
+ ├─ PlacementExtractor
23
+ └─ OutputFormatters
24
+ ```
25
+
26
+ A workflow is **executable command definition** that chains service calls.
27
+
28
+ ## Plugin Service Modules
29
+
30
+ Plugins provide **cohesive sets of related services**:
31
+
32
+ ### Example: Inventory-Aware BOM Plugin
33
+
34
+ **Plugin Identity:**
35
+ - Name: `inventory_aware_bom`
36
+ - Version: `2.0.0`
37
+ - Provides: Services for BOM generation with inventory matching
38
+
39
+ **Module: KiCadReader**
40
+ Services for reading KiCad project data:
41
+ ```python
42
+ KICAD_PROJECT = readProject(path) # Load project metadata
43
+ COMPONENT_LIST = readSchematic(project) # Extract component list
44
+ PCB_DATA = readPCB(project) # Load PCB for placement
45
+ HIERARCHY = readHierarchy(project) # Get hierarchical structure
46
+ ```
47
+
48
+ **Module: InventoryService**
49
+ Services for inventory data access:
50
+ ```python
51
+ INVENTORY = readInventoryCSV(path) # CSV format
52
+ INVENTORY = readInventoryExcel(path) # Excel format
53
+ INVENTORY = readInventoryNumbers(path) # Apple Numbers
54
+ INVENTORY = readInventoryAirtable(url, key) # Airtable API
55
+ INVENTORY = combineInventories([inv1, inv2, ...]) # Federate multiple sources
56
+ INVENTORY = createInventoryFromProject(components) # Generate from schematic
57
+ ```
58
+
59
+ **Module: BOMGenerator**
60
+ Services for BOM generation and manipulation:
61
+ ```python
62
+ BOM = createBOM(components, inventory, options) # Generate BOM with matching
63
+ BOM = filterBOM(bom, filters) # Apply filters (SMD-only, etc.)
64
+ BOM = enrichBOM(bom, fabricator) # Add fabricator-specific fields
65
+ MATCHES = matchComponent(component, inventory) # Find matches for one component
66
+ ```
67
+
68
+ **Module: OutputFormatter**
69
+ Services for output generation:
70
+ ```python
71
+ printBOMcsv(bom, path, fields) # CSV output
72
+ printBOMtable(bom, fields) # Console table
73
+ printBOMjson(bom, path) # JSON output
74
+ ```
75
+
76
+ ## Workflow Definitions
77
+
78
+ Workflows are **named compositions of service calls**. They define the command the user invokes.
79
+
80
+ ### Workflow: `bom`
81
+
82
+ **Description:** Generate Bill of Materials for fabrication
83
+
84
+ **Composition:**
85
+ ```python
86
+ workflow "bom":
87
+ # Input phase
88
+ project = KiCadReader.readProject(args.project)
89
+ components = KiCadReader.readSchematic(project)
90
+
91
+ # Optional inventory
92
+ if args.inventory:
93
+ inventory = InventoryService.combineInventories([
94
+ InventoryService.readInventory(path) for path in args.inventory
95
+ ])
96
+ else:
97
+ inventory = InventoryService.createInventoryFromProject(components)
98
+
99
+ # Processing phase
100
+ options = BOMOptions(
101
+ fabricator=args.fabricator,
102
+ smd_only=args.smd_only,
103
+ verbose=args.verbose
104
+ )
105
+ bom = BOMGenerator.createBOM(components, inventory, options)
106
+
107
+ if args.fabricator:
108
+ bom = BOMGenerator.enrichBOM(bom, args.fabricator)
109
+
110
+ # Output phase
111
+ if args.output == "console":
112
+ OutputFormatter.printBOMtable(bom, args.fields)
113
+ else:
114
+ OutputFormatter.printBOMcsv(bom, args.output, args.fields)
115
+ ```
116
+
117
+ **CLI Mapping:**
118
+ ```bash
119
+ jbom bom project/ -i inv.csv --jlc -o bom.csv
120
+ ```
121
+
122
+ ### Workflow: `inventory`
123
+
124
+ **Description:** Generate or update inventory from projects
125
+
126
+ **Composition:**
127
+ ```python
128
+ workflow "inventory":
129
+ # Scan multiple projects
130
+ all_components = []
131
+ for project_path in args.projects:
132
+ project = KiCadReader.readProject(project_path)
133
+ components = KiCadReader.readSchematic(project)
134
+ all_components.extend(components)
135
+
136
+ # Create inventory
137
+ inventory = InventoryService.createInventoryFromProject(
138
+ all_components,
139
+ merge_existing=args.update
140
+ )
141
+
142
+ # Output
143
+ OutputFormatter.printInventorycsv(inventory, args.output)
144
+ ```
145
+
146
+ ### Workflow: `pos`
147
+
148
+ **Description:** Generate placement file for fabrication
149
+
150
+ **Composition:**
151
+ ```python
152
+ workflow "pos":
153
+ project = KiCadReader.readProject(args.project)
154
+ pcb_data = KiCadReader.readPCB(project)
155
+
156
+ placement = PlacementExtractor.extractPlacement(pcb_data)
157
+
158
+ if args.fabricator:
159
+ placement = PlacementExtractor.transformForFabricator(
160
+ placement,
161
+ args.fabricator
162
+ )
163
+
164
+ OutputFormatter.printPOScsv(placement, args.output)
165
+ ```
166
+
167
+ ## Architectural Components
168
+
169
+ ### 1. Plugin Discovery & Loading
170
+
171
+ **Responsibility:** Find and load plugin service modules
172
+
173
+ **Two-tier approach:**
174
+
175
+ #### Core Plugins (Bundled)
176
+ Built into jBOM, always available:
177
+ ```
178
+ src/jbom/plugins/
179
+ ├── inventory_aware_bom/
180
+ │ ├── __init__.py # Plugin metadata
181
+ │ ├── kicad_reader.py # Service module
182
+ │ ├── inventory_service.py # Service module
183
+ │ ├── bom_generator.py # Service module
184
+ │ └── workflows.yaml # Workflow definitions
185
+ └── placement/
186
+ ├── __init__.py
187
+ ├── pcb_reader.py
188
+ └── workflows.yaml
189
+ ```
190
+
191
+ **Discovery:** Static filesystem scan at startup
192
+ - Scan `src/jbom/plugins/` directory
193
+ - Load each plugin's `__init__.py` for metadata
194
+ - Import service modules
195
+ - Load workflow definitions from `workflows.yaml`
196
+
197
+ #### Extended Plugins (User-installed)
198
+ Installed via `jbom install <plugin>`, stored locally:
199
+ ```
200
+ ~/.jbom/plugins/
201
+ ├── validation/
202
+ │ ├── plugin.yaml # Plugin metadata
203
+ │ ├── rules_engine.py # Service module
204
+ │ └── workflows.yaml # Workflow definitions
205
+ └── cost_analysis/
206
+ ├── plugin.yaml
207
+ ├── pricing_service.py
208
+ └── workflows.yaml
209
+ ```
210
+
211
+ **Installation mechanism:**
212
+ ```bash
213
+ # Install plugin from git repo
214
+ $ jbom install github:user/jbom-validation-plugin
215
+
216
+ # Or from local directory
217
+ $ jbom install /path/to/plugin/
218
+
219
+ # List installed plugins
220
+ $ jbom plugins list
221
+
222
+ # Uninstall
223
+ $ jbom uninstall validation
224
+ ```
225
+
226
+ **What `jbom install` does:**
227
+ 1. Downloads/copies plugin to `~/.jbom/plugins/<name>/`
228
+ 2. Validates plugin structure (required files, metadata)
229
+ 3. Registers in `~/.jbom/plugins/registry.json`
230
+ 4. Available on next jBOM invocation
231
+
232
+ **Discovery:** Static filesystem scan at startup
233
+ - Scan `~/.jbom/plugins/` directory
234
+ - Load each plugin's `plugin.yaml` for metadata
235
+ - Import service modules (Python modules in plugin directory)
236
+ - Load workflow definitions
237
+
238
+ **Combined registry:**
239
+ ```python
240
+ # At startup, jBOM builds:
241
+ service_registry = {
242
+ 'inventory_aware_bom.KiCadReader.readProject': <function>,
243
+ 'inventory_aware_bom.BOMGenerator.createBOM': <function>,
244
+ 'validation.RulesEngine.validateDesign': <function>, # User plugin
245
+ # ...
246
+ }
247
+
248
+ workflow_registry = {
249
+ 'bom': <workflow_definition>,
250
+ 'pos': <workflow_definition>,
251
+ 'validate': <workflow_definition>, # From user plugin
252
+ # ...
253
+ }
254
+ ```
255
+
256
+ **No runtime pip dependency.** All discovery is filesystem-based, explicit installation.
257
+
258
+ **Result:**
259
+ - Service registry: All available service functions (core + installed)
260
+ - Workflow registry: All available commands (core + installed)
261
+
262
+ ### 2. CLI Parser & Validator
263
+
264
+ **Responsibility:** Parse command line, validate against workflow schema
265
+
266
+ **Process:**
267
+ 1. User invokes: `jbom <workflow> <args>`
268
+ 2. Lookup workflow definition from registry
269
+ 3. Extract required arguments from workflow composition
270
+ 4. Parse and validate CLI arguments
271
+ 5. Return validated argument object
272
+
273
+ **Example for `bom` workflow:**
274
+ ```
275
+ Required: project (from readProject)
276
+ Optional: inventory (from readInventory), output, fabricator, smd_only
277
+ ```
278
+
279
+ ### 3. Workflow Executor
280
+
281
+ **Responsibility:** Execute workflow composition with validated arguments
282
+
283
+ **Challenge:** Turn workflow definition into executable code
284
+
285
+ **Options for workflow execution:**
286
+
287
+ #### Option A: Python Functions (Current Pattern)
288
+ Workflows are Python functions that call services:
289
+ ```python
290
+ # Plugin provides
291
+ def execute_bom_workflow(args):
292
+ project = readProject(args.project)
293
+ components = readSchematic(project)
294
+ # ... rest of workflow
295
+ ```
296
+
297
+ **Problem:** Requires writing Python code for each workflow. Not composable.
298
+
299
+ #### Option B: Declarative Workflow DSL
300
+ Workflows defined in data (YAML/JSON), interpreted at runtime:
301
+ ```yaml
302
+ workflow:
303
+ name: bom
304
+ steps:
305
+ - service: KiCadReader.readProject
306
+ input: args.project
307
+ output: project
308
+
309
+ - service: KiCadReader.readSchematic
310
+ input: project
311
+ output: components
312
+
313
+ - service: InventoryService.readInventory
314
+ input: args.inventory
315
+ output: inventory
316
+ optional: true
317
+
318
+ - service: BOMGenerator.createBOM
319
+ inputs: [components, inventory, options]
320
+ output: bom
321
+
322
+ - service: OutputFormatter.printBOMcsv
323
+ inputs: [bom, args.output, args.fields]
324
+ ```
325
+
326
+ **Executor interprets steps:**
327
+ ```python
328
+ def execute_workflow(workflow_def, args):
329
+ context = {'args': args} # Execution context
330
+
331
+ for step in workflow_def.steps:
332
+ service = resolve_service(step.service)
333
+ inputs = [context[inp] for inp in step.inputs]
334
+ result = service(*inputs)
335
+ context[step.output] = result
336
+
337
+ return context
338
+ ```
339
+
340
+ **Benefits:**
341
+ - Workflows are data, not code
342
+ - Can be extended/modified without Python
343
+ - Easy to validate, visualize, test
344
+ - Plugins provide services, users compose workflows
345
+
346
+ #### Option C: Pipeline/Chain Pattern
347
+ Workflows are chains of transformations:
348
+ ```python
349
+ workflow = (
350
+ Pipeline()
351
+ .then(readProject)
352
+ .then(readSchematic)
353
+ .then(lambda comps: createBOM(comps, inventory, options))
354
+ .then(lambda bom: printBOMcsv(bom, output, fields))
355
+ )
356
+
357
+ result = workflow.execute(args.project)
358
+ ```
359
+
360
+ **Compromise:** Balance between flexibility and simplicity.
361
+
362
+ ## Architectural Decisions
363
+
364
+ ### 1. Workflow Definition Format
365
+
366
+ **Decision:** Pipeline/Chain pattern (Option C)
367
+
368
+ **Rationale:** Maps well to Gherkin BDD methodology. Workflows are readable sequences of transformations that mirror test scenarios.
369
+
370
+ ```python
371
+ workflow_bom = (
372
+ Pipeline()
373
+ .then(readProject) # GIVEN a KiCad project
374
+ .then(readSchematic) # WHEN I read the schematic
375
+ .then(lambda c: createBOM(c, [])) # AND generate a BOM
376
+ .then(lambda b: printBOMcsv(b, out)) # THEN output CSV
377
+ )
378
+ ```
379
+
380
+ Gherkin scenario structure directly informs workflow steps.
381
+
382
+ ### 2. Service Discovery Mechanism
383
+
384
+ **Decision:** Service registry with late binding
385
+
386
+ **Rationale:** Enables plugin discoverability, testing with mocks, and flexibility in service resolution.
387
+
388
+ ```python
389
+ # Registry built at startup from filesystem scan
390
+ service_registry = ServiceRegistry()
391
+ service_registry.scan("src/jbom/plugins/") # Core plugins
392
+ service_registry.scan("~/.jbom/plugins/") # User plugins
393
+
394
+ # Workflows resolve services from registry
395
+ readProject = service_registry.get("kicad.readProject")
396
+ ```
397
+
398
+ No runtime pip dependency - purely filesystem-based discovery.
399
+
400
+ ### 3. Argument Mapping
401
+
402
+ **Decision:** Start simple, evolve to hybrid
403
+
404
+ **Bootstrap:** Manual mapping in workflow code
405
+ ```python
406
+ def execute_bom(args):
407
+ project = readProject(args.project)
408
+ # Explicit, clear, no magic
409
+ ```
410
+
411
+ **Future evolution:** Naming conventions with overrides (once patterns emerge)
412
+ ```python
413
+ # Automatic: args.project → project parameter
414
+ # Override: @arg("inventory", source="args.inventory_files")
415
+ ```
416
+
417
+ **Rationale:** Don't get stuck in fiddly details during bootstrap. Encapsulate mapping logic for future evolution.
418
+
419
+ ### 4. Error Handling Strategy
420
+
421
+ **Decision:** Exception propagation with workflow-level handlers
422
+
423
+ **Rationale:** Python-idiomatic, familiar, sufficient for needs.
424
+
425
+ ```python
426
+ try:
427
+ result = workflow.execute(args)
428
+ except FileNotFoundError as e:
429
+ print(f"Error: {e}")
430
+ return 1
431
+ except ValidationError as e:
432
+ print(f"Invalid input: {e}")
433
+ return 1
434
+ ```
435
+
436
+ Workflows can add specific error handling for their domain.
437
+
438
+ ## Key Architectural Relationships
439
+
440
+ **User ↔ CLI Parser**
441
+ - User invokes workflow command with arguments
442
+ - Parser validates against workflow schema
443
+
444
+ **CLI Parser ↔ Workflow Registry**
445
+ - Parser queries: "What arguments does workflow X need?"
446
+ - Registry provides workflow definition
447
+
448
+ **Workflow Executor ↔ Service Registry**
449
+ - Executor queries: "Get me service plugin.module.service"
450
+ - Registry provides callable service function
451
+
452
+ **Workflow ↔ Plugin Services**
453
+ - Workflow orchestrates multiple service calls
454
+ - Services are stateless, composable functions
455
+
456
+ **Plugin ↔ Service Modules**
457
+ - Plugin groups related services into modules
458
+ - Modules provide cohesive service sets
459
+
460
+ ## Bootstrap Plan: Build New System in Parallel
461
+
462
+ **Strategy:** Don't evolve existing jBOM - build new system from scratch using BDD/TDD.
463
+
464
+ **Approach:**
465
+ 1. **Gherkin features** define expected behavior
466
+ 2. **Functional tests** (step definitions) validate behavior
467
+ 3. **Implementation** makes tests pass
468
+ 4. **Unit tests** for core abstractions (after patterns emerge)
469
+
470
+ **Parallel development:** Existing jBOM continues working while new system is built.
471
+
472
+ ### Step 1: Minimal Viable Core (Week 1-2)
473
+
474
+ **Goal:** Prove the pattern with simplest possible implementation
475
+
476
+ **Features to implement:**
477
+ ```gherkin
478
+ Feature: Read KiCad Project
479
+ Scenario: Read schematic components
480
+ Given a KiCad project at "test_project/"
481
+ When I read the schematic
482
+ Then I should get a list of components
483
+ And each component should have reference, value, footprint
484
+
485
+ Feature: Simple BOM Generation
486
+ Scenario: Generate BOM without inventory
487
+ Given a KiCad project with components
488
+ When I generate a BOM
489
+ Then components should be grouped by value and footprint
490
+ And output should include reference, quantity, value, footprint
491
+ ```
492
+
493
+ **Services to implement:**
494
+ - `KiCadReader.readProject(path)` - Read .kicad_sch
495
+ - `KiCadReader.readSchematic(project)` - Extract components
496
+ - `BOMGenerator.createBOM(components)` - Group components
497
+ - `OutputFormatter.printBOMcsv(bom, path)` - Write CSV
498
+ - `OutputFormatter.printBOMtable(bom)` - Console table
499
+
500
+ **Workflow to implement:**
501
+ ```python
502
+ workflow_bom = (
503
+ Pipeline()
504
+ .then(KiCadReader.readProject)
505
+ .then(KiCadReader.readSchematic)
506
+ .then(BOMGenerator.createBOM)
507
+ .then(OutputFormatter.printBOMcsv)
508
+ )
509
+ ```
510
+
511
+ **Infrastructure:**
512
+ - Basic plugin discovery (scan `src/jbom/plugins/`)
513
+ - Service registry (dict of name → function)
514
+ - Workflow registry (dict of name → Pipeline)
515
+ - CLI parser (argparse, just `jbom bom <project>`)
516
+
517
+ **Success criteria:**
518
+ ```bash
519
+ $ jbom-new bom test_project/ -o bom.csv
520
+ # Works! Generated minimal BOM
521
+ ```
522
+
523
+ ### Step 2: Add POS Workflow (Week 3)
524
+
525
+ **Goal:** Prove plugin system works for second workflow
526
+
527
+ **New features:**
528
+ ```gherkin
529
+ Feature: Generate Placement File
530
+ Scenario: Extract component placement from PCB
531
+ Given a KiCad PCB file
532
+ When I generate placement data
533
+ Then output should include designator, x, y, rotation, side
534
+ ```
535
+
536
+ **New services:**
537
+ - `KiCadReader.readPCB(project)` - Read .kicad_pcb
538
+ - `PlacementExtractor.extractPlacement(pcb)` - Get coordinates
539
+ - `OutputFormatter.printPOScsv(placement, path)` - Write POS
540
+
541
+ **New workflow:**
542
+ ```python
543
+ workflow_pos = (
544
+ Pipeline()
545
+ .then(KiCadReader.readProject)
546
+ .then(KiCadReader.readPCB)
547
+ .then(PlacementExtractor.extractPlacement)
548
+ .then(OutputFormatter.printPOScsv)
549
+ )
550
+ ```
551
+
552
+ **Success criteria:**
553
+ - Two workflows work independently
554
+ - Share `KiCadReader` services (reusability proven)
555
+ - Both discoverable via `jbom-new --help`
556
+
557
+ ### Step 3: Configuration System (Week 4)
558
+
559
+ **Goal:** Prove configuration mechanism
560
+
561
+ **Features:**
562
+ ```gherkin
563
+ Feature: Configuration
564
+ Scenario: Load user configuration
565
+ Given a config file at "~/.jbom/config.yaml"
566
+ When jBOM starts
567
+ Then services should use configured defaults
568
+ ```
569
+
570
+ **Config structure:**
571
+ ```yaml
572
+ core:
573
+ default_output_format: csv
574
+
575
+ plugins:
576
+ bom:
577
+ group_by: [value, footprint]
578
+ ```
579
+
580
+ **Config access:**
581
+ ```python
582
+ config = Config.load() # Discovers and merges files
583
+ plugin_config = config.plugins.bom
584
+ ```
585
+
586
+ ### Step 4: User Plugin Installation (Week 5)
587
+
588
+ **Goal:** Prove extensibility with user plugins
589
+
590
+ **Implement:**
591
+ - `jbom-new install /path/to/plugin/` command
592
+ - Copy plugin to `~/.jbom/plugins/`
593
+ - Scan both core and user plugins at startup
594
+ - Simple validation (required files exist)
595
+
596
+ **Test with sample plugin:**
597
+ ```bash
598
+ $ jbom-new install ./sample-validation-plugin/
599
+ Installed validation v1.0.0
600
+
601
+ $ jbom-new plugins list
602
+ Core plugins:
603
+ bom v1.0.0
604
+ pos v1.0.0
605
+
606
+ User plugins:
607
+ validation v1.0.0
608
+
609
+ $ jbom-new validate project/
610
+ # User plugin workflow executes!
611
+ ```
612
+
613
+ ### Step 5: Inventory Integration (Week 6+)
614
+
615
+ **Goal:** Add back real-world complexity incrementally
616
+
617
+ **Add services:**
618
+ - `InventoryService.readInventoryCSV(path)`
619
+ - `BOMGenerator.matchInventory(component, inventory)`
620
+ - Enhanced `createBOM(components, inventory)`
621
+
622
+ **Extend BOM workflow:**
623
+ ```python
624
+ workflow_bom_with_inventory = (
625
+ Pipeline()
626
+ .then(KiCadReader.readProject)
627
+ .then(KiCadReader.readSchematic)
628
+ .then(lambda c: (c, InventoryService.readInventoryCSV(args.inventory)))
629
+ .then(lambda data: BOMGenerator.createBOM(*data))
630
+ .then(OutputFormatter.printBOMcsv)
631
+ )
632
+ ```
633
+
634
+ **Continue adding:**
635
+ - Excel/Numbers inventory formats
636
+ - Fabricator enrichment
637
+ - Advanced filtering
638
+ - Field customization
639
+
640
+ **Each addition:**
641
+ 1. Write Gherkin scenario
642
+ 2. Implement step definitions
643
+ 3. Make tests pass
644
+ 4. Refine based on learnings
645
+
646
+ ### Success Metrics
647
+
648
+ **After Step 4, we have:**
649
+ - ✅ Proven pattern (services + workflows + plugins)
650
+ - ✅ BDD/TDD foundation
651
+ - ✅ Working CLI
652
+ - ✅ Configuration system
653
+ - ✅ Plugin extensibility
654
+ - ✅ Clean, testable code
655
+ - ✅ Foundation for expansion
656
+
657
+ **Open questions answered by implementation:**
658
+ - Service granularity → Emerges from use
659
+ - Data contracts → Defined by what tests need
660
+ - Execution context → Becomes clear in pipeline implementation
661
+ - Many others → Solved when encountered, not prematurely
662
+
663
+ ## Open Questions
664
+
665
+ 1. **Service granularity:** How fine-grained should services be? (e.g., one service per file format, or one generic readInventory?)
666
+
667
+ 2. **State management:** Are services stateless functions, or can they maintain state?
668
+
669
+ 3. **Plugin compatibility:** How do we handle core vs. user plugins depending on each other?
670
+
671
+ 4. **Workflow testing:** How do we test workflow compositions without executing full pipeline?
672
+
673
+ 5. **Configuration:** Where does configuration live - in workflows, services, or both?
674
+
675
+ 6. **Data contracts:** What are the types of KICAD_PROJECT, INVENTORY, BOM, etc.? How are they validated?
676
+
677
+ 7. **Execution context:** How do workflows share data between steps (context object, return values, global state)?
678
+
679
+ 8. **Plugin sandboxing:** Should user plugins run in restricted environment? Security model?
680
+
681
+ 9. **Plugin updates:** How does `jbom install` handle updates? Version pinning?
682
+
683
+ ## Conclusion
684
+
685
+ jBOM architecture centers on **workflows composing plugin services**:
686
+
687
+ - **Plugins provide services** - Reusable functions grouped by domain (KiCad access, inventory, BOM generation)
688
+ - **Workflows compose services** - Named command definitions that chain service calls
689
+ - **CLI maps to workflows** - User commands execute workflow compositions
690
+
691
+ The key challenge is **workflow execution** - how to practically turn workflow definitions into executable code. This requires resolving services from registry, mapping arguments, handling errors, and managing execution context.
692
+
693
+ Start with explicit Python workflows, evolve to more declarative/composable patterns as understanding deepens.