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.
- {jbom-4.5.1/src/jbom.egg-info → jbom-4.7.0}/PKG-INFO +1 -1
- jbom-4.7.0/docs/design/workflow-architecture.md +693 -0
- {jbom-4.5.1 → jbom-4.7.0}/pyproject.toml +24 -2
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/__version__.py +1 -1
- {jbom-4.5.1 → jbom-4.7.0/src/jbom.egg-info}/PKG-INFO +1 -1
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/SOURCES.txt +1 -1
- jbom-4.5.1/docs/design/plugin-architecture-proposal.md +0 -432
- {jbom-4.5.1 → jbom-4.7.0}/LICENSE +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/MANIFEST.in +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/README.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/CHANGELOG.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/CONTRIBUTING.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/README.configuration.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/README.developer.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/README.man1.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/README.man3.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/README.man4.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/README.man5.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/README.tests.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/WARP.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/BDD_AXIOMS.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/BEHAVE_SUBDIRECTORY_LOADING.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/PROJECT_INPUT_RESOLUTION_TESTS.feature +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/README.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/back_annotation_requirements.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/component_rotation_correction_requirements.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/comprehensive_fault_testing_requirements.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/fabrication_platform_requirements.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/active/fabricator_integration_requirements.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/completed/federated_inventory_requirements.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/completed/inventory_management_requirements.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/development_tasks.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/development_notes/sample_detailed_validation_report.txt +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/jbom-config-schema.yaml +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/test_diagnostics_phase1-2_complete.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/docs/test_failure_diagnostics_proposal.md +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/kicad_jbom_plugin.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/setup.cfg +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/setup.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/__main__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/api.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/base.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/annotate.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/bom.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/inventory.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/inventory_search.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/pos.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/commands/builtin/search.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/common.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/formatting.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/main.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/cli/main_old.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/config.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/config_fabricators.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/constants.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/fabricators.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/fields.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/fields_system.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/generator.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/options.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/output.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/packages.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/sexp_parser.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/types.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/utils.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/common/values.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/defaults.yaml +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/generic.fab.yaml +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/jlc.fab.yaml +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/pcbway.fab.yaml +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/config/fabricators/seeed.fab.yaml +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/generators/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/generators/bom.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/generators/pos.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/inventory.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/jlc_loader.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/pcb.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/pcb_model.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/project_inventory.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/loaders/schematic.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/annotator.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/classifier.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/component_types.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/inventory_enricher.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/inventory_matcher.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/processors/search_result_scorer.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/sch_api/model.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/search/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/search/filter.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom/search/mouser.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/dependency_links.txt +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/entry_points.txt +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/requires.txt +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/src/jbom.egg-info/top_level.txt +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/config/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_config_cli_integration.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_config_system.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_config_units.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/config/test_hierarchical_config.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_back_annotation.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_base.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_bom.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_bom_errors.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_classification.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_fabrication.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_fabricator.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_federation.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_inventory_formats.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_inventory_generation.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_pos.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_pos_config.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_pos_errors.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_schematic_edge_cases.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_functional_search.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_integration_all_interfaces.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/functional/test_kicad_plugin.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/test_integration_projects.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/__init__.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_api_enriched_inventory.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_api_v3.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_classifier.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_cli.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_cli_inventory_command.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_common_fields.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_federated_inventory.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_generators_bom.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_generic_fabricator.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_inventory_enricher.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_inventory_matching.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_inventory_numbers_real.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_loaders_schematic.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_pcbway_poc.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_position.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_processors_value_parsing.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_project_inventory.py +0 -0
- {jbom-4.5.1 → jbom-4.7.0}/tests/unit/test_search_result_scorer.py +0 -0
|
@@ -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.
|