diff --git a/fusion_plating/docs/superpowers/plans/2026-04-29-step-library-audit.md b/fusion_plating/docs/superpowers/plans/2026-04-29-step-library-audit.md
new file mode 100644
index 00000000..7f740a00
--- /dev/null
+++ b/fusion_plating/docs/superpowers/plans/2026-04-29-step-library-audit.md
@@ -0,0 +1,1858 @@
+# Step Library Expansion + Per-Recipe Configurability + Audit Coverage — Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Cover the full plating-shop workflow with new Step Kinds + audit-grade default measurements, expose per-recipe configurability for every prompt + master toggle + custom prompts + reset-to-library, surface office-to-operator instructions in the simple recipe editor with library/recipe override, wire all of it through to runtime + CoC report + tablet, and battle-test end-to-end on entech.
+
+**Architecture:** Library `fp.step.template` is the smart default; recipe `fusion.plating.process.node` is the final say. New Selection values added to step kinds + input types. New per-row `collect` boolean (with master `collect_measurements` on the node) drives runtime filtering. Office instructions stay as `description` Html on both library and recipe; recipe override falls through to library when empty. Battle test exercises the full chain library → recipe → job → CoC report.
+
+**Tech Stack:** Odoo 19, Python (models, computes, migrations), OWL (recipe editor), QWeb (CoC report), JSON-RPC controllers.
+
+**Spec:** [`docs/superpowers/specs/2026-04-29-step-library-audit-design.md`](../specs/2026-04-29-step-library-audit-design.md)
+
+**Deploy target:** entech (LXC 111 on pve-worker5, native Odoo, DB `admin`, addons at `/mnt/extra-addons/custom/`).
+
+---
+
+## File Map
+
+| File | Status | Responsibility |
+|---|---|---|
+| `fusion_plating/models/fp_step_template.py` | MOD | Extend `default_kind`, expand `DEFAULT_INPUTS_BY_KIND`, add `action_add_common_audit_fields` |
+| `fusion_plating/models/fp_step_template_input.py` | MOD | Add 4 new input types to selection |
+| `fusion_plating/models/fp_process_node.py` | MOD | Add `collect_measurements` on node; add `collect` + `template_input_id` on input model; mirror new input types |
+| `fusion_plating/views/fp_step_template_views.xml` | MOD | Add audit-fields button; relabel description |
+| `fusion_plating/data/fp_step_template_data.xml` | NEW | Seed templates for the 8 new Step Kinds |
+| `fusion_plating/migrations/19.0.18.7.0/post-migrate.py` | NEW | Backfill collect/collect_measurements; re-seed defaults |
+| `fusion_plating/static/src/js/simple_recipe_editor.js` | MOD | Render Instructions + Measurements expansions; collect badge |
+| `fusion_plating/static/src/xml/simple_recipe_editor.xml` | MOD | OWL templates for new affordances |
+| `fusion_plating/static/src/scss/simple_recipe_editor.scss` | MOD | Styles for expansions and badges |
+| `fusion_plating/controllers/simple_recipe_controller.py` | MOD | New endpoints for toggle/edit/reset |
+| `fusion_plating_jobs/wizards/fp_job_step_input_wizard.py` | MOD | Filter `collect=True`; mirror new input types |
+| `fusion_plating_jobs/wizards/fp_job_step_input_wizard_views.xml` | MOD | Per-input-type widgets |
+| `fusion_plating_shopfloor/static/src/...` (tablet OWL) | MOD | Per-type rendering |
+| `fusion_plating_reports/views/report_coc_chronological.xml` | MOD | Render branches for new types; filter `collect=True` |
+| `fusion_plating/scripts/bt_step_library_audit.py` | NEW | 18-assertion battle-test |
+| `fusion_plating/__manifest__.py` | MOD | Bump to `19.0.18.7.0` |
+| `fusion_plating_jobs/__manifest__.py` | MOD | Bump version |
+| `fusion_plating_reports/__manifest__.py` | MOD | Bump version |
+
+---
+
+## Phase A — Model Foundations
+
+### Task A1: Add new input types to library template input model
+
+**Files:**
+- Modify: `fusion_plating/models/fp_step_template_input.py`
+
+- [ ] **Step 1: Open the file and locate the `input_type` Selection at line ~27**
+
+- [ ] **Step 2: Extend the selection with 4 new types**
+
+Replace:
+
+```python
+ input_type = fields.Selection([
+ ('text', 'Text'),
+ ('number', 'Number'),
+ ('boolean', 'Yes/No'),
+ ('selection', 'Selection'),
+ ('date', 'Date / Time'),
+ ('signature', 'Signature'),
+ ('time_hms', 'Time (HH:MM:SS)'),
+ ('time_seconds', 'Time (seconds)'),
+ ('temperature', 'Temperature'),
+ ('thickness', 'Thickness'),
+ ('pass_fail', 'Pass / Fail'),
+ ], string='Input Type', required=True, default='text')
+```
+
+With:
+
+```python
+ input_type = fields.Selection([
+ ('text', 'Text'),
+ ('number', 'Number'),
+ ('boolean', 'Yes/No'),
+ ('selection', 'Selection'),
+ ('date', 'Date / Time'),
+ ('signature', 'Signature'),
+ ('time_hms', 'Time (HH:MM:SS)'),
+ ('time_seconds', 'Time (seconds)'),
+ ('temperature', 'Temperature'),
+ ('thickness', 'Thickness'),
+ ('pass_fail', 'Pass / Fail'),
+ ('photo', 'Photo'),
+ ('multi_point_thickness', 'Multi-Point Thickness (avg)'),
+ ('bath_chemistry_panel', 'Bath Chemistry Panel'),
+ ('ph', 'pH'),
+ ], string='Input Type', required=True, default='text')
+```
+
+- [ ] **Step 3: Verify Python parses cleanly**
+
+Run: `python -c "import ast; ast.parse(open('K:/Github/Odoo-Modules/fusion_plating/fusion_plating/models/fp_step_template_input.py').read())"`
+Expected: silent (no error).
+
+### Task A2: Mirror new input types on the recipe-step (process node) input model
+
+**Files:**
+- Modify: `fusion_plating/models/fp_process_node.py:621-640` (the `input_type` Selection on the inline input model)
+
+- [ ] **Step 1: Locate the input_type Selection block (search for `('thickness', 'Thickness')`)**
+
+- [ ] **Step 2: Add the four new entries above the closing bracket**
+
+Replace:
+
+```python
+ input_type = fields.Selection(
+ [
+ ('text', 'Text'),
+ ('number', 'Number'),
+ ('boolean', 'Yes/No'),
+ ('selection', 'Selection'),
+ ('time_hms', 'Time (HH:MM:SS)'),
+ ('time_seconds', 'Time (seconds)'),
+ ('temperature', 'Temperature'),
+ ('thickness', 'Thickness'),
+ ('pass_fail', 'Pass / Fail'),
+ ('date', 'Date / Time'),
+ ('signature', 'Signature'),
+ ('location_picker', 'Location Picker'),
+ ('customer_wo', 'Customer WO #'),
+ ],
+ string='Input Type',
+ required=True,
+ default='text',
+ )
+```
+
+With:
+
+```python
+ input_type = fields.Selection(
+ [
+ ('text', 'Text'),
+ ('number', 'Number'),
+ ('boolean', 'Yes/No'),
+ ('selection', 'Selection'),
+ ('time_hms', 'Time (HH:MM:SS)'),
+ ('time_seconds', 'Time (seconds)'),
+ ('temperature', 'Temperature'),
+ ('thickness', 'Thickness'),
+ ('pass_fail', 'Pass / Fail'),
+ ('date', 'Date / Time'),
+ ('signature', 'Signature'),
+ ('location_picker', 'Location Picker'),
+ ('customer_wo', 'Customer WO #'),
+ ('photo', 'Photo'),
+ ('multi_point_thickness', 'Multi-Point Thickness (avg)'),
+ ('bath_chemistry_panel', 'Bath Chemistry Panel'),
+ ('ph', 'pH'),
+ ],
+ string='Input Type',
+ required=True,
+ default='text',
+ )
+```
+
+### Task A3: Add `collect`, `template_input_id`, `collect_measurements` fields
+
+**Files:**
+- Modify: `fusion_plating/models/fp_process_node.py`
+
+- [ ] **Step 1: Locate the inline input model class (it's a nested model inside fp_process_node.py — search for `_name = 'fusion.plating.process.node.input'`).**
+
+- [ ] **Step 2: Add `collect` and `template_input_id` after the `compliance_tag` field (around line 698)**
+
+Insert immediately before the class' closing `_sql_constraints` or the next field block:
+
+```python
+ # ===== Sub 12d — per-recipe configurability =============================
+ collect = fields.Boolean(
+ string='Collect This Measurement',
+ default=True,
+ help='Toggle off to skip this prompt at runtime without deleting it. '
+ 'Recipe authors use this to opt out of library-seeded prompts '
+ 'without affecting the library itself.',
+ )
+ template_input_id = fields.Many2one(
+ 'fp.step.template.input',
+ string='Source Library Prompt',
+ ondelete='set null',
+ help='Set when this row was snapshot-copied from a library template '
+ 'prompt. Powers "Reset to Library Defaults" — rows where this '
+ 'is False are treated as recipe-only custom prompts and '
+ 'survive the reset.',
+ )
+```
+
+- [ ] **Step 3: Add `collect_measurements` boolean on the parent node model**
+
+Locate the main `fusion.plating.process.node` model class (search for `_name = 'fusion.plating.process.node'` — the node itself, not the input child). Find a sensible location near the existing `description` field (search for `description = fields.Html`) and add immediately after:
+
+```python
+ collect_measurements = fields.Boolean(
+ string='Collect Measurements at Runtime',
+ default=True,
+ help='Master switch. When off, the operator wizard skips this step '
+ 'entirely (no input prompts shown). Use for housekeeping steps '
+ 'or when no measurement is needed for this recipe.',
+ )
+```
+
+- [ ] **Step 4: Verify Python parses**
+
+Run: `python -c "import ast; ast.parse(open('K:/Github/Odoo-Modules/fusion_plating/fusion_plating/models/fp_process_node.py').read())"`
+Expected: silent.
+
+- [ ] **Step 5: Commit Phase A**
+
+```bash
+cd K:/Github/Odoo-Modules/fusion_plating
+git add fusion_plating/models/fp_step_template_input.py fusion_plating/models/fp_process_node.py
+git commit -m "model(step-library): add 4 new input types + per-recipe collect toggles"
+```
+
+---
+
+## Phase B — Library DEFAULT_INPUTS_BY_KIND + 8 New Step Kinds
+
+### Task B1: Add 8 new Step Kinds to default_kind Selection
+
+**Files:**
+- Modify: `fusion_plating/models/fp_step_template.py:77-94`
+
+- [ ] **Step 1: Locate the `default_kind` Selection field (around line 77)**
+
+- [ ] **Step 2: Replace it with the expanded list**
+
+Replace:
+
+```python
+ default_kind = fields.Selection([
+ ('cleaning', 'Cleaning'),
+ ('etch', 'Etch'),
+ ('rinse', 'Rinse'),
+ ('plate', 'Plating'),
+ ('bake', 'Bake'),
+ ('inspect', 'Inspection'),
+ ('racking', 'Racking'),
+ ('derack', 'De-Racking'),
+ ('mask', 'Masking'),
+ ('demask', 'De-Masking'),
+ ('dry', 'Drying'),
+ ('wbf_test', 'Water Break Free Test'),
+ ('final_inspect', 'Final Inspection'),
+ ('ship', 'Shipping'),
+ ('gating', 'Gating'),
+ ('contract_review', 'Contract Review (QA-005)'),
+ ], string='Step Kind', help='Drives sane-default input seeding.')
+```
+
+With:
+
+```python
+ default_kind = fields.Selection([
+ ('receiving', 'Receiving / Incoming Inspection'),
+ ('contract_review', 'Contract Review (QA-005)'),
+ ('racking', 'Racking'),
+ ('mask', 'Masking'),
+ ('cleaning', 'Cleaning'),
+ ('electroclean', 'Electroclean'),
+ ('etch', 'Etch / Activation'),
+ ('rinse', 'Rinse'),
+ ('strike', 'Strike (Wood\'s Nickel / Activation)'),
+ ('plate', 'Plating'),
+ ('replenishment', 'Tank Replenishment'),
+ ('wbf_test', 'Water Break Free Test'),
+ ('dry', 'Drying'),
+ ('bake', 'Bake (HE Relief / Stress Relief)'),
+ ('demask', 'De-Masking'),
+ ('derack', 'De-Racking'),
+ ('inspect', 'Inspection'),
+ ('hardness_test', 'Hardness Test (HV / HK / HRC)'),
+ ('adhesion_test', 'Adhesion Test'),
+ ('salt_spray', 'Salt Spray / Corrosion Test'),
+ ('final_inspect', 'Final Inspection'),
+ ('packaging', 'Packaging / Pre-Ship'),
+ ('ship', 'Shipping'),
+ ('gating', 'Gating'),
+ ], string='Step Kind', help='Drives sane-default input seeding.')
+```
+
+The list is now ordered roughly by typical workflow phase to make it easier for shop authors to scan.
+
+### Task B2: Expand DEFAULT_INPUTS_BY_KIND for all kinds
+
+**Files:**
+- Modify: `fusion_plating/models/fp_step_template.py:140-213`
+
+- [ ] **Step 1: Replace the `DEFAULT_INPUTS_BY_KIND` dict with the full expanded version**
+
+Replace the entire dict (lines 140 through the closing `}` around line 213) with:
+
+```python
+ DEFAULT_INPUTS_BY_KIND = {
+ 'receiving': [
+ {'name': 'Qty Received', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 10, 'required': True},
+ {'name': 'Qty Rejected', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 20},
+ {'name': 'Customer PO# Verified', 'input_type': 'boolean', 'sequence': 30},
+ {'name': 'Packing Slip #', 'input_type': 'text', 'sequence': 40},
+ {'name': 'Condition Notes', 'input_type': 'text', 'sequence': 50},
+ {'name': 'Damage Photo', 'input_type': 'photo', 'sequence': 60},
+ {'name': 'Inspector Initials', 'input_type': 'signature',
+ 'sequence': 70, 'required': True},
+ ],
+ 'cleaning': [
+ {'name': 'Actual Time', 'input_type': 'time_seconds',
+ 'target_unit': 's', 'sequence': 10},
+ {'name': 'Actual Temperature', 'input_type': 'temperature',
+ 'target_unit': 'f', 'sequence': 20},
+ {'name': 'Bath ID', 'input_type': 'text', 'sequence': 30},
+ {'name': 'Ultrasonic On', 'input_type': 'boolean', 'sequence': 40},
+ {'name': 'Titration Done', 'input_type': 'boolean', 'sequence': 50},
+ ],
+ 'electroclean': [
+ {'name': 'Actual Time', 'input_type': 'time_seconds',
+ 'target_unit': 's', 'sequence': 10},
+ {'name': 'Actual Temperature', 'input_type': 'temperature',
+ 'target_unit': 'f', 'sequence': 20},
+ {'name': 'Amperage', 'input_type': 'number', 'sequence': 30,
+ 'hint': 'A'},
+ {'name': 'Voltage', 'input_type': 'number', 'sequence': 40,
+ 'hint': 'V'},
+ {'name': 'Current Density', 'input_type': 'number', 'sequence': 50,
+ 'hint': 'ASF (A per sq ft)'},
+ {'name': 'Polarity', 'input_type': 'selection', 'sequence': 60,
+ 'selection_options': 'anodic,cathodic,periodic'},
+ {'name': 'Bath ID', 'input_type': 'text', 'sequence': 70},
+ ],
+ 'etch': [
+ {'name': 'Actual Time', 'input_type': 'time_seconds',
+ 'target_unit': 's', 'sequence': 10},
+ {'name': 'Actual Temperature', 'input_type': 'temperature',
+ 'target_unit': 'f', 'sequence': 20},
+ {'name': 'Acid Concentration', 'input_type': 'number', 'sequence': 30,
+ 'hint': '% or g/L'},
+ {'name': 'Bath ID', 'input_type': 'text', 'sequence': 40},
+ {'name': 'HE Risk Flag', 'input_type': 'boolean', 'sequence': 50,
+ 'hint': 'Hydrogen Embrittlement risk for high-strength steel'},
+ ],
+ 'rinse': [
+ {'name': 'Rinse Type', 'input_type': 'selection', 'sequence': 10,
+ 'selection_options': 'cascade,spray,DI,city'},
+ {'name': 'Conductivity', 'input_type': 'number', 'sequence': 20,
+ 'hint': 'µS/cm — required for DI rinses'},
+ {'name': 'Actual Time', 'input_type': 'time_seconds',
+ 'target_unit': 's', 'sequence': 30},
+ ],
+ 'strike': [
+ {'name': 'Actual Time', 'input_type': 'time_seconds',
+ 'target_unit': 's', 'sequence': 10},
+ {'name': 'Actual Temperature', 'input_type': 'temperature',
+ 'target_unit': 'f', 'sequence': 20},
+ {'name': 'Amperage', 'input_type': 'number', 'sequence': 30,
+ 'hint': 'A'},
+ {'name': 'Voltage', 'input_type': 'number', 'sequence': 40,
+ 'hint': 'V'},
+ {'name': 'Current Density', 'input_type': 'number', 'sequence': 50,
+ 'hint': 'ASF'},
+ {'name': 'Bath ID', 'input_type': 'text', 'sequence': 60},
+ ],
+ 'plate': [
+ {'name': 'Actual Time', 'input_type': 'time_hms',
+ 'target_unit': 'min', 'sequence': 10},
+ {'name': 'Actual Temperature', 'input_type': 'temperature',
+ 'target_unit': 'f', 'sequence': 20},
+ {'name': 'Bath ID', 'input_type': 'text', 'sequence': 30},
+ {'name': 'pH', 'input_type': 'ph', 'sequence': 40},
+ {'name': 'Bath Concentration', 'input_type': 'number', 'sequence': 50,
+ 'hint': 'g/L'},
+ {'name': 'Current Density', 'input_type': 'number', 'sequence': 60,
+ 'hint': 'ASF — electroplate only'},
+ {'name': 'Plating Thickness', 'input_type': 'multi_point_thickness',
+ 'target_unit': 'in', 'sequence': 70},
+ ],
+ 'replenishment': [
+ {'name': 'Bath ID', 'input_type': 'text', 'sequence': 10,
+ 'required': True},
+ {'name': 'Chemistry Added', 'input_type': 'text', 'sequence': 20,
+ 'hint': 'name + amount, e.g. "Nickel sulfamate 500mL"'},
+ {'name': 'pH Before', 'input_type': 'ph', 'sequence': 30},
+ {'name': 'pH After', 'input_type': 'ph', 'sequence': 40},
+ {'name': 'Concentration Before', 'input_type': 'number', 'sequence': 50},
+ {'name': 'Concentration After', 'input_type': 'number', 'sequence': 60},
+ {'name': 'Operator Initials', 'input_type': 'signature',
+ 'sequence': 70, 'required': True},
+ ],
+ 'wbf_test': [
+ {'name': 'Result', 'input_type': 'pass_fail', 'sequence': 10,
+ 'required': True},
+ {'name': 'Retest Count', 'input_type': 'number', 'sequence': 20},
+ {'name': 'Photo on FAIL', 'input_type': 'photo', 'sequence': 30},
+ ],
+ 'dry': [
+ {'name': 'Dry Method', 'input_type': 'selection', 'sequence': 10,
+ 'selection_options': 'hot air,oven,spin'},
+ {'name': 'Actual Time', 'input_type': 'time_seconds',
+ 'target_unit': 's', 'sequence': 20},
+ {'name': 'Actual Temperature', 'input_type': 'temperature',
+ 'target_unit': 'f', 'sequence': 30},
+ ],
+ 'bake': [
+ {'name': 'Time In', 'input_type': 'date', 'sequence': 10},
+ {'name': 'Time Out', 'input_type': 'date', 'sequence': 20},
+ {'name': 'Actual Temperature', 'input_type': 'temperature',
+ 'target_unit': 'f', 'sequence': 30},
+ {'name': 'Oven ID', 'input_type': 'text', 'sequence': 40},
+ {'name': 'Chart Recorder File', 'input_type': 'photo', 'sequence': 50,
+ 'hint': 'Attach AMS-2759 chart-recorder file'},
+ ],
+ 'racking': [
+ {'name': 'Actual Qty', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 10, 'required': True},
+ {'name': 'Rack ID', 'input_type': 'text', 'sequence': 20},
+ {'name': 'Masking Applied', 'input_type': 'boolean', 'sequence': 30},
+ {'name': 'Photo of Racked Load', 'input_type': 'photo', 'sequence': 40},
+ ],
+ 'derack': [
+ {'name': 'Actual Qty', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 10},
+ {'name': 'Mask Removal Method', 'input_type': 'selection', 'sequence': 20,
+ 'selection_options': 'mechanical,solvent,thermal,not applicable'},
+ {'name': 'Residue Check', 'input_type': 'pass_fail', 'sequence': 30},
+ ],
+ 'mask': [
+ {'name': 'Actual Qty', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 10},
+ {'name': 'Mask Material', 'input_type': 'selection', 'sequence': 20,
+ 'selection_options': 'Microshield,latex tape,vinyl plugs,wax,other'},
+ {'name': 'Photo of Masked Parts', 'input_type': 'photo', 'sequence': 30},
+ ],
+ 'demask': [
+ {'name': 'Residue Check', 'input_type': 'pass_fail', 'sequence': 10},
+ {'name': 'Surface Condition', 'input_type': 'selection', 'sequence': 20,
+ 'selection_options': 'clean,marks,needs rework'},
+ ],
+ 'inspect': [
+ {'name': 'Result', 'input_type': 'pass_fail', 'sequence': 10,
+ 'required': True},
+ {'name': 'Defect Type', 'input_type': 'selection', 'sequence': 20,
+ 'selection_options': 'pitting,burn,blister,peel,missing coverage,none'},
+ {'name': 'Thickness Sample', 'input_type': 'thickness',
+ 'target_unit': 'in', 'sequence': 30},
+ {'name': 'Photo', 'input_type': 'photo', 'sequence': 40},
+ {'name': 'Inspector Signature', 'input_type': 'signature', 'sequence': 50},
+ ],
+ 'hardness_test': [
+ {'name': 'Test Load', 'input_type': 'number', 'sequence': 10,
+ 'hint': 'gf'},
+ {'name': 'Readings (HV/HK/HRC)', 'input_type': 'multi_point_thickness',
+ 'sequence': 20, 'hint': 'Three indents minimum'},
+ {'name': 'Equipment ID', 'input_type': 'text', 'sequence': 30},
+ {'name': 'Last Calibration Date', 'input_type': 'date', 'sequence': 40},
+ ],
+ 'adhesion_test': [
+ {'name': 'Test Method', 'input_type': 'selection', 'sequence': 10,
+ 'selection_options': 'bend,tape,burnish,file'},
+ {'name': 'Result', 'input_type': 'pass_fail', 'sequence': 20,
+ 'required': True},
+ {'name': 'Photo of Coupon', 'input_type': 'photo', 'sequence': 30},
+ ],
+ 'salt_spray': [
+ {'name': 'Test Duration', 'input_type': 'number', 'sequence': 10,
+ 'hint': 'hours'},
+ {'name': 'Result', 'input_type': 'pass_fail', 'sequence': 20,
+ 'required': True},
+ {'name': 'Red Rust %', 'input_type': 'number', 'sequence': 30},
+ {'name': 'White Corrosion %', 'input_type': 'number', 'sequence': 40},
+ {'name': 'Lab Report', 'input_type': 'photo', 'sequence': 50,
+ 'hint': 'Attach scanned lab report'},
+ ],
+ 'final_inspect': [
+ {'name': 'Outgoing Part Count Verified',
+ 'input_type': 'boolean', 'sequence': 10},
+ {'name': 'Qty Accepted', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 20},
+ {'name': 'Qty Rejected', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 30},
+ {'name': 'Defect Categorization', 'input_type': 'selection', 'sequence': 35,
+ 'selection_options': 'pitting,burn,blister,peel,missing coverage,dimensional,none'},
+ {'name': 'Actual Coating Thickness',
+ 'input_type': 'multi_point_thickness',
+ 'target_unit': 'in', 'sequence': 40},
+ {'name': 'Dimensional Verification', 'input_type': 'pass_fail',
+ 'sequence': 45},
+ {'name': 'Surface Finish (Ra)', 'input_type': 'number', 'sequence': 47,
+ 'hint': 'µin'},
+ {'name': 'Pass/Fail', 'input_type': 'pass_fail', 'sequence': 50,
+ 'required': True},
+ {'name': 'Inspector Signature', 'input_type': 'signature', 'sequence': 60},
+ ],
+ 'packaging': [
+ {'name': 'Packaging Type', 'input_type': 'selection', 'sequence': 10,
+ 'selection_options': 'VCI bag,bubble wrap,separator paper,custom crate,other'},
+ {'name': 'Qty Per Package', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 20},
+ {'name': 'Package Count', 'input_type': 'number', 'sequence': 30},
+ {'name': 'Cert Package Included', 'input_type': 'boolean', 'sequence': 40},
+ {'name': 'Customer-Supplied Packaging', 'input_type': 'boolean',
+ 'sequence': 50},
+ ],
+ 'ship': [
+ {'name': 'Outgoing Qty', 'input_type': 'number',
+ 'target_unit': 'each', 'sequence': 10, 'required': True},
+ {'name': 'Carrier', 'input_type': 'selection', 'sequence': 20,
+ 'selection_options': 'UPS,FedEx,Purolator,Customer Pickup,Other'},
+ {'name': 'Tracking #', 'input_type': 'text', 'sequence': 30},
+ {'name': 'BoL #', 'input_type': 'text', 'sequence': 40},
+ {'name': 'Photo of Sealed Shipment', 'input_type': 'photo',
+ 'sequence': 50},
+ ],
+ 'gating': [],
+ 'contract_review': [
+ {'name': 'Reviewer Initials', 'input_type': 'signature', 'sequence': 10},
+ {'name': 'Date Reviewed', 'input_type': 'date', 'sequence': 20},
+ {'name': 'QA-005 Approved', 'input_type': 'pass_fail', 'sequence': 30},
+ ],
+ }
+```
+
+### Task B3: Add `action_add_common_audit_fields` method
+
+**Files:**
+- Modify: `fusion_plating/models/fp_step_template.py` (after `action_seed_default_inputs`, around line 234)
+
+- [ ] **Step 1: Append after `action_seed_default_inputs`**
+
+```python
+ COMMON_AUDIT_FIELDS = [
+ {'name': 'Operator Initials', 'input_type': 'signature',
+ 'required': True, 'sequence': 800},
+ {'name': 'Bath ID', 'input_type': 'text', 'sequence': 810},
+ {'name': 'Photo on Failure', 'input_type': 'photo', 'sequence': 820,
+ 'hint': 'upload only if failure observed'},
+ {'name': 'Equipment ID', 'input_type': 'text', 'sequence': 830},
+ ]
+
+ def action_add_common_audit_fields(self):
+ """Idempotently append the common audit fields to this template.
+ Skips rows whose name already exists. Logs to chatter.
+ """
+ Input = self.env['fp.step.template.input']
+ for tpl in self:
+ existing_names = set(tpl.input_template_ids.mapped('name'))
+ added = []
+ for spec in self.COMMON_AUDIT_FIELDS:
+ if spec['name'] in existing_names:
+ continue
+ Input.create({
+ 'template_id': tpl.id,
+ **spec,
+ })
+ added.append(spec['name'])
+ if added:
+ tpl.message_post(
+ body=_('Added common audit fields: %s') % ', '.join(added),
+ message_type='notification',
+ subtype_xmlid='mail.mt_note',
+ )
+ return True
+```
+
+- [ ] **Step 2: Verify Python parses**
+
+Run: `python -c "import ast; ast.parse(open('K:/Github/Odoo-Modules/fusion_plating/fusion_plating/models/fp_step_template.py').read())"`
+Expected: silent.
+
+- [ ] **Step 3: Commit Phase B model changes**
+
+```bash
+git add fusion_plating/models/fp_step_template.py
+git commit -m "model(step-library): add 8 new Step Kinds + expanded defaults + audit-fields one-click"
+```
+
+---
+
+## Phase C — Library Views
+
+### Task C1: Surface "Add Common Audit Fields" button + relabel description
+
+**Files:**
+- Modify: `fusion_plating/views/fp_step_template_views.xml`
+
+- [ ] **Step 1: In the form view's `