feat(fusion_plating): extend resolver + auto-classify hook on process node
Resolver (fp_resolve_step_kind) extensions: - New aliases: blasting/bead blast/media blast variants, adhesion testing, corrosion testing, lab testing, strip process, chemical conversion, trivalent chromate, plug the threaded holes, air dry, desmut, soak clean, cleaner, nickel strike/strip - Parenthetical suffix stripping - "Masking (If Required)" resolves through "masking", "Incoming Inspection (Standard)" through "incoming inspection", "Trivalent Chromate Conversion (A-14 / A)" through "trivalent chromate conversion" - New RESOLVER_KIND_TO_ACTIVE_KIND map translates the resolver's vocabulary (cleaning/electroclean/etch/rinse/strike/dry/wbf_test -> wet_process) so the resolver output lands on active kinds only Auto-classify hook on fusion.plating.process.node: - _fp_autoclassify_kind() upgrades kind_id when current is 'other' AND name resolves via the resolver. Idempotent - never overrides a non-'other' kind. Skip via context flag fp_skip_kind_autoclassify - Wired into create() and write() (only fires when name or kind_id changed on write) - Side-effects: recipe duplication via copy() auto-corrects newly copied nodes; Simple/Tree editor authoring auto-classifies as soon as the name is saved Spec: docs/superpowers/specs/2026-05-24-recipe-cleanup-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
@@ -282,6 +283,38 @@ _STARTER_KIND_BY_NAME = {
|
||||
'ready for post-plate inspection': 'gating',
|
||||
'ready for final inspection': 'gating',
|
||||
'ready for shipping': 'gating',
|
||||
# 2026-05-24 — Recipe cleanup additions (spec
|
||||
# 2026-05-24-recipe-cleanup-design.md). Covers names the existing
|
||||
# resolver didn't know that turned up in the entech recipes audit.
|
||||
# Blasting variants
|
||||
'blasting': 'blast',
|
||||
'bead blast': 'blast',
|
||||
'bead blasting': 'blast',
|
||||
'media blast': 'blast',
|
||||
'media blasting': 'blast',
|
||||
# Inspection variants
|
||||
'adhesion test coupon': 'inspect',
|
||||
'adhesion testing': 'inspect',
|
||||
'corrosion testing': 'inspect',
|
||||
'lab testing': 'inspect',
|
||||
'check sulfamate nickel area': 'inspect',
|
||||
'pre-measurements': 'inspect',
|
||||
'pre measurements': 'inspect',
|
||||
'hot water porosity': 'inspect',
|
||||
# Strip / chemical conversion / plugging (wet line)
|
||||
'strip process': 'wet_process',
|
||||
'strip process - al': 'wet_process',
|
||||
'nickel strip - aluminum line': 'wet_process',
|
||||
'chemical conversion': 'wet_process',
|
||||
'trivalent chromate conversion': 'wet_process',
|
||||
'plug the threaded holes': 'mask',
|
||||
# Misc wet-line variants seen on entech recipes
|
||||
'air dry': 'dry',
|
||||
'desmut': 'etch',
|
||||
'soak clean': 'cleaning',
|
||||
'cleaner': 'cleaning',
|
||||
'nickel strike': 'plate',
|
||||
'nickel strip': 'plate',
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +323,9 @@ def fp_resolve_step_kind(name):
|
||||
case. Used by both the seeder and the migration backfill so we don't
|
||||
have two slightly-different lookup paths.
|
||||
|
||||
Handles parenthetical suffixes like "(Standard)", "(If Required)",
|
||||
"(A-14 / A)" by stripping them and re-trying the lookup.
|
||||
|
||||
Returns the kind str or None when no match.
|
||||
"""
|
||||
if not name:
|
||||
@@ -297,6 +333,12 @@ def fp_resolve_step_kind(name):
|
||||
key = name.strip().lower()
|
||||
if key in _STARTER_KIND_BY_NAME:
|
||||
return _STARTER_KIND_BY_NAME[key]
|
||||
# Parenthetical strip — "Masking (If Required)" → "masking",
|
||||
# "Incoming Inspection (Standard)" → "incoming inspection",
|
||||
# "Trivalent Chromate Conversion (A-14 / A)" → "trivalent chromate conversion".
|
||||
bare = re.sub(r'\s*\([^)]*\)\s*', ' ', key).strip()
|
||||
if bare and bare != key and bare in _STARTER_KIND_BY_NAME:
|
||||
return _STARTER_KIND_BY_NAME[bare]
|
||||
# Gating "Ready for / Ready For" prefix — anything starting with that
|
||||
# is a gating node regardless of the destination step name.
|
||||
if key.startswith('ready for ') or key.startswith('ready '):
|
||||
@@ -304,6 +346,38 @@ def fp_resolve_step_kind(name):
|
||||
return None
|
||||
|
||||
|
||||
# Translates resolver kind output to the active fp.step.kind.code values.
|
||||
# The resolver still returns the OLD vocabulary (cleaning, electroclean,
|
||||
# etch, rinse, strike, dry, wbf_test) which were deactivated in
|
||||
# 19.0.20.6.0 — those roll up to the active wet_process kind. Other
|
||||
# codes pass through 1:1. Used by the auto-classify hook on
|
||||
# fusion.plating.process.node + the recipe-cleanup migration
|
||||
# (fusion_plating_jobs 19.0.10.26.0).
|
||||
RESOLVER_KIND_TO_ACTIVE_KIND = {
|
||||
# Wet-line kinds → wet_process (active rollup)
|
||||
'cleaning': 'wet_process',
|
||||
'electroclean': 'wet_process',
|
||||
'etch': 'wet_process',
|
||||
'rinse': 'wet_process',
|
||||
'strike': 'wet_process',
|
||||
'dry': 'wet_process',
|
||||
'wbf_test': 'wet_process',
|
||||
# 1:1 mappings (kind exists and is active)
|
||||
'contract_review': 'contract_review',
|
||||
'mask': 'mask',
|
||||
'racking': 'racking',
|
||||
'plate': 'plate',
|
||||
'bake': 'bake',
|
||||
'derack': 'derack',
|
||||
'demask': 'demask',
|
||||
'inspect': 'inspect',
|
||||
'final_inspect': 'final_inspect',
|
||||
'ship': 'ship',
|
||||
'gating': 'gating',
|
||||
'blast': 'blast',
|
||||
}
|
||||
|
||||
|
||||
def _seed_step_library_if_empty(env):
|
||||
"""Sub 12a — seed fp.step.template starter library.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user