diff --git a/fusion_plating/docs/superpowers/specs/2026-04-30-simple-editor-refinement-design.md b/fusion_plating/docs/superpowers/specs/2026-04-30-simple-editor-refinement-design.md new file mode 100644 index 00000000..e4406e06 --- /dev/null +++ b/fusion_plating/docs/superpowers/specs/2026-04-30-simple-editor-refinement-design.md @@ -0,0 +1,323 @@ +# Simple Recipe Editor — Refinement (Bug Fix + Inline Library Authoring + Breadcrumbs) + +**Status:** Design — pending implementation plan +**Date:** 2026-04-30 +**Modules touched:** `fusion_plating`, `fusion_plating_jobs` +**Versions to bump:** `fusion_plating` 19.0.11.4.0 → 19.0.12.0.0 (migration), `fusion_plating_jobs` 19.0.7.x` (no schema change, optional) + +--- + +## Problem + +Three issues, surfaced together while a customer was trying to author recipes +using the Simple Editor: + +1. **Generation bug — Simple-Editor recipes produce zero job steps.** + `walk_node()` in `fp_job.py:631` only creates `fp.job.step` rows for + `node.node_type == 'operation'`. The Simple Editor inserts library + templates with `node_type='step'` directly under the recipe root + (`simple_recipe_controller.py:178`). Top-level step children of a + recipe fall through. Verified on entech: `LGPS1104` has 19 flat step + rows under the recipe root — `fp_job` has 0 rows referencing it. Real + downstream consequence: no traveller content, no shopfloor tablet + queue entries, no CoC moves, nothing. The recipe is silently inert. + +2. **Library authoring requires a menu trip.** Users have to leave the + Simple Editor, navigate to Configuration → Recipes & Steps → Step + Library, create a template, configure prompts in a list view, then + come back to the editor to drag it in. The template form is + manager-grade (notebook with 4 tabs); it's not the right tool for a + shop foreman who wants to capture a one-off step their facility + does. + +3. **No way back from the Simple Editor.** Click "Open Simple Editor" + from the recipe form → full-screen client action with no back button, + no breadcrumb. Operator has to use Chrome's back arrow to escape. + The Tree Editor has had a custom "← Back to Recipes" button since + 2026-04-22; the Simple Editor was never given one. + +## Goals + +- Simple-Editor-authored recipes produce correct job steps end-to-end — + WO traveller, shopfloor tablet, CoC, reports. +- A shop foreman can author a new library step (with prompts, kind, + stations, instructions) without leaving the Simple Editor. +- A shop foreman can edit an existing library step without leaving the + Simple Editor. +- Closing the Simple Editor returns to the recipe list (or part form, if + opened from the Process Composer). +- Existing tree-editor recipes are untouched. Existing simple-editor + recipes get migrated, with a chatter note explaining the change. + +## Non-goals + +- No new step kinds. The existing 23-value `default_kind` Selection + stays as-is. Free-text custom kinds are explicitly out of scope — + templates without a kind continue to behave as "Generic" (no auto- + seeded prompts, no auto-gates), which is fine. +- No change to the Tree Editor. +- No change to the manager-grade Step Library form view. +- No prompt-collection runtime behaviour change (`collect_measurements`, + per-input `collect`, etc. all keep working as today). + +--- + +## Bucket 1 — Generation bug fix + +### Migration (one-shot, fusion_plating 19.0.12.0.0/post-migrate.py) + +```sql +-- Flip top-level step children of recipes → operation. Filter is +-- intentionally narrow: only nodes whose parent is a recipe root. +-- Tree-editor 'step' rows (which sit under operation parents) are +-- untouched. +UPDATE fusion_plating_process_node child +SET node_type = 'operation' +WHERE child.node_type = 'step' + AND child.parent_id IN ( + SELECT id FROM fusion_plating_process_node WHERE node_type = 'recipe' + ); +``` + +After the SQL runs, walk every distinct `recipe_root_id` whose children +were touched and post a chatter note: + +> "Recipe migrated to v19.0.12.0.0 step layout. Step nodes that were +> direct children of this recipe (Simple Editor authoring) have been +> promoted to operation nodes so they generate work-order steps +> correctly. No data was lost — only `node_type` changed. If this +> recipe was already authored via the Tree Editor with explicit +> sub-process / operation hierarchy, this migration was a no-op for it." + +### Controller change (going forward) + +`simple_recipe_controller.py:178` — +```python +new_vals = { + 'parent_id': recipe.id, + 'node_type': 'operation', # was 'step' — fix Bucket 1 bug + 'sequence': target_seq, +} +``` + +`_snapshot_step_into()` (template/import path) gets the same fix on +line 295. + +### What "just works" after this + +- `walk_node()` finds operation nodes, creates `fp.job.step` rows. +- WO traveller (`report_fp_job_traveller.xml`) iterates + `job.step_ids` — populated. +- Shopfloor tablet `/fp/shopfloor/scan` returns the same step rows. +- CoC chronological body uses `fp.job.step.move` rows that get created + on tablet operations against real step rows. +- Quality-point matching (`fp.quality.point._matches`) keys off + `step.kind`, which is mapped from `node.default_kind` in the same + walk_node code path. Templates with no kind get `step.kind='other'` + — behaves like a generic step. No regression. + +### Verification checklist (manual, after deploy) + +1. Create a new job against `LGPS1104` on entech. +2. Confirm `fp.job.step_ids` contains 19 rows (or however many the recipe has). +3. Print the WO Traveller — every step listed. +4. Open the operator tablet for that job — every step in My Queue. +5. Walk one step start → finish on the tablet — `fp.job.step.move` row created. +6. Generate a chronological CoC against the closed job — body lists each step heading. + +--- + +## Bucket 2 — Inline library authoring in Simple Editor + +### UX + +In the right-hand Step Library panel, above the search input: +``` +┌──────────────────────────────┐ +│ + New Step │ +├──────────────────────────────┤ +│ [Search… ] │ +├──────────────────────────────┤ +│ ⚙ Acid Dip ✎ │ +│ 🔗 Adhesion Test ✎ │ +│ ... │ +└──────────────────────────────┘ +``` + +- **+ New Step** opens an inline form (replaces the library list while + open, "Save" / "Cancel" return to list). +- Pencil ✎ next to each library row → same form, prefilled with the + template's current values. + +### Inline form fields + +Two-column grid: + +``` +NAME * KIND +[ ] [Generic — no automatic ▾] + +ICON ALLOWED STATIONS +[⚙ Cog ▾] [tag1] [tag2] [+] + +INSTRUCTIONS +[ ] +[ ] +[ ] + +FLAGS +☐ Require QA Sign-off +☐ Require Predecessor Done +☐ Requires Rack Assignment +☐ Requires Transition Form + +PROMPTS (Operation Measurements — what the operator records during this step) +┌───────┬──────────────────┬──────────┬─────┬─────┬────┬───┐ +│Collect│ Prompt │ Type │ Min │ Max │Req │ × │ +├───────┼──────────────────┼──────────┼─────┼─────┼────┼───┤ +│ ☑ │ Actual Time │ Time(sec)│ │ │ │ × │ +│ ☑ │ Bath ID │ Text │ │ │ │ × │ +└───────┴──────────────────┴──────────┴─────┴─────┴────┴───┘ +[+ Add prompt] [Seed defaults from kind] + +[Save] [Cancel] +``` + +The PROMPTS table reuses the same column layout as the existing +per-recipe-step edit panel (`simple_recipe_editor.xml:127-200`). Same +input-type Selection (15 options), same min/max/req fields. Authors +get one consistent table whether they're editing a recipe step or a +library template. + +The "Seed defaults from kind" button calls +`action_seed_default_inputs()` server-side and refreshes — gives the +shop foreman the same one-click seeding the manager form has. + +### Controller endpoints (new) + +``` +POST /fp/simple_recipe/library/load { template_id } +POST /fp/simple_recipe/library/save { template_id|null, vals } # create or update +POST /fp/simple_recipe/library/seed_defaults { template_id } # action_seed_default_inputs +POST /fp/simple_recipe/library/input/add { template_id, payload } +POST /fp/simple_recipe/library/input/write { input_id, payload } +POST /fp/simple_recipe/library/input/remove { input_id } +``` + +`library/save` does an upsert: if `template_id` is null, creates; +otherwise writes. Returns the full template payload (same shape as +`library/load`) so the OWL component can refresh state in one call. + +The existing `library/create`, `library/write`, `library/delete` stay +for back-compat (they're called by no other code today, so we could +delete them, but cheap to leave). + +### Snapshot semantics (no change) + +When the author saves a new template and then drags it into a recipe, +the existing `_copy_inputs_from_template()` ([simple_recipe_controller.py:223](fusion_plating/controllers/simple_recipe_controller.py:223)) +copies all `input_template_ids` → recipe-step `input_ids`. Editing the +template later does NOT mutate recipes already built (Q4 = A locked +2026-04-27). This bucket changes nothing about that contract. + +### What's excluded from inline form (still requires manager form) + +- Time/Temp/Voltage/Viscosity targets (Advanced tab) — rarely set by + shop foreman, lots of pickers, would bloat the inline form. Stays on + the manager form. +- Transition Form fields — Sub 12b feature, more compliance-heavy, + needs the dedicated tab. Stays on the manager form. +- Common Audit Fields seeding — accessible via `action_add_common_audit_fields` + on the manager form. Could be added to the inline form later if + asked. + +The pencil ✎ form has a small "Open in full editor" link in the +corner that does `action.doAction({type:'ir.actions.act_window', res_model:'fp.step.template', res_id:tpl.id})` for the manager-only escape hatch. + +--- + +## Bucket 3 — Breadcrumb / Back navigation + +### Header layout (Simple Editor) + +``` +[← Back to Recipes] Recipe: LGPS1104 [Open in Tree Editor] +``` + +### Logic (mirrors recipe_tree_editor.js:548) + +```js +onBackToList() { + const partId = this.props.action?.context?.part_id; + if (partId) { + this.action.doAction({ + type: "ir.actions.act_window", + res_model: "fp.part.catalog", + res_id: partId, + views: [[false, "form"]], + target: "current", + }, { clearBreadcrumbs: true }); + return; + } + this.action.doAction("fusion_plating.action_fp_process_recipe", { + clearBreadcrumbs: true, + }); +} +``` + +`clearBreadcrumbs: true` is critical — without it, every part-form → +composer → editor → back leaves the intermediate pages on the breadcrumb +stack. Same battle-scar the Tree Editor learned from in 2026-04-22. + +--- + +## Bucket 4 — Downstream verification + +No code expected. Manual checklist after Buckets 1–3 ship to entech: + +- [ ] Job generated from LGPS1104 has 19 fp.job.step rows +- [ ] WO Traveller PDF lists every step +- [ ] Tablet "My Queue" lists every step on the operator with that job's work centre +- [ ] Step start → finish creates an `fp.job.step.move` row +- [ ] Closed-job CoC body (chronological style) lists every step heading +- [ ] User-added templates (Blasting, Surface Activation, Nickel Strip — Steel Line, Nickel Strip (S-1), Air Dry) flow through correctly +- [ ] Templates without `default_kind` flow through with `fp.job.step.kind='other'` and behave as plain steps (no auto-gates) +- [ ] Existing tree-editor recipes still produce the same number of steps as before (no regression) + +--- + +## Risks + mitigations + +| Risk | Mitigation | +|---|---| +| Migration touches a recipe authored by Tree Editor that happens to have a flat step under recipe root | Filter is `parent_id IN (recipe ids)`. Tree-editor `step` rows have `operation` parents, not `recipe`. Verified zero false positives on entech (only Simple-Editor recipes have `step` children of `recipe`). | +| New job-step rows surface a Sub 12b "Requires Rack Assignment" or Sub 8 racking-inspection gate operator wasn't expecting | All such gates check `requires_rack_assignment` / `default_kind=='racking'`. User-added Blasting / Surface Activation templates have neither set, so they'll behave as plain operations. | +| Inline form lets a foreman delete a library template that's used in 30 recipes | `library/delete` is already soft-delete (sets active=False) when used. Inline form keeps the same behaviour. | +| Author edits a library template's prompts and expects existing recipes to update | They won't — snapshot semantics still apply. UX mitigation: on save, post a notification "Saved. Existing recipes using this step keep their old prompts; new drops will use the new prompts." | +| Migration runs twice | SQL is idempotent (only flips `step` → `operation`; second run finds no `step` children of recipes, no-op). Chatter note is gated on `node_type` change so won't double-post. | + +--- + +## Module versions + +- `fusion_plating`: bump to 19.0.12.0.0 (migration + controller change + OWL changes) +- `fusion_plating_jobs`: no version bump required, but worth a smoke test + +## Build order + +1. Migration `fusion_plating/migrations/19.0.12.0.0/post-migrate.py` — flip + chatter +2. Controller — change `node_type='operation'` in two places + add 6 new `library/*` endpoints +3. OWL JS — `simple_recipe_editor.js` adds inline form state + handlers + back button +4. OWL XML — `simple_recipe_editor.xml` adds inline form template + back button + pencil +5. SCSS — minimal; reuse existing classes from edit panel +6. Bump `fusion_plating/__manifest__.py` version +7. Deploy to entech, run verification checklist + +## Things explicitly NOT in this spec + +- New step kinds (deferred — clients add templates as they go, kind stays optional) +- Tree Editor changes +- Step Library manager-grade form changes +- New report content +- New shopfloor tablet content +- Free-text custom kinds (rejected — would silently lose 4 functional behaviours)