fix(oracle-r4): curation contradiction + wizard view enrichment

Oracle round-7 remediation, items 2 + 7:

- flowCuration.NON_BUSINESS_SOURCE_CONTEXTS no longer lists
  EA2_DRAFT_PROCESS:process_creation (it's in STARTABLE_SOURCE_CONTEXTS).
  Adds CANVAS_AGENT_MEMORY + CANVAS_AGENT_VAULT_ROOT to NON_BUSINESS so
  vault docs never leak into hubs / catalogue.
- wizardApi.createView now accepts a fields list and emits proper field
  defs (name slug, label, type from {string,number,boolean,textarea}).
- Wizard.handleDataSave rebuilds every step's view with the user's
  draft.fields after Generate phase, then rewires the presentation
  edges to the new views. Steps now expose real business fields, not a
  generic 'Notes' textarea.
This commit is contained in:
2026-06-14 15:07:57 +04:00
parent 810d011b80
commit cc037a149e
3 changed files with 30 additions and 11 deletions
+2 -1
View File
@@ -30,10 +30,11 @@ const NON_BUSINESS_SOURCE_CONTEXTS = new Set([
"CANVAS_CHAT_INBOX",
"CANVAS_ATTENDANCE_ROOT",
"CANVAS_ATTENDANCE_SITE",
"CANVAS_AGENT_MEMORY",
"CANVAS_AGENT_VAULT_ROOT",
"CANVAS_SEED_PROCESS",
"CANVAS_SEED_STEP",
"EA2_WIZARD_STEP",
"EA2_DRAFT_PROCESS:process_creation",
]);
export const STARTABLE_FLOW_KEYS = new Set<string>([
+13 -7
View File
@@ -139,9 +139,20 @@ export const wizardApi = {
},
};
},
createView(node: { display_name: string }): CreateOp {
createView(node: { display_name: string; fields?: { name: string; type: string }[] }): CreateOp {
const slug = node.display_name.toLowerCase().replace(/[^a-z0-9]+/g, "_").slice(0, 30) || "view";
const name = `${slug}_view_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
const TYPE_MAP: Record<string, string> = { string: "string", number: "number", boolean: "boolean", textarea: "textarea" };
const fieldDefs =
node.fields && node.fields.length > 0
? node.fields
.filter((f) => f.name && f.name.trim())
.map((f) => {
const fieldSlug = f.name.toLowerCase().replace(/[^a-z0-9]+/g, "_").slice(0, 40);
const label = f.name.charAt(0).toUpperCase() + f.name.slice(1);
return { name: fieldSlug, label, type: TYPE_MAP[f.type] || "string", required: true, description: label };
})
: [{ name: "notes", label: "Notes", type: "textarea", required: false, description: "Notes for this step" }];
return {
op: "create",
coll: "view",
@@ -153,12 +164,7 @@ export const wizardApi = {
description: null,
source_context: null,
channel: "web",
layout: {
layout: "form",
fields: [
{ name: "notes", label: "Notes", type: "textarea", required: false, description: "Notes" },
],
},
layout: { layout: "form", fields: fieldDefs },
actions: [{ label: "Submit", action: "submit" }],
},
};
+15 -3
View File
@@ -144,13 +144,25 @@ export default function Wizard() {
if (!draft.flowKey || !actor) return;
setWorking(true);
try {
// In a real app we'd save proper data models here.
// For the spike we just move forward and do a dummy update
await wizardApi.updateDraftConfig(draft.flowKey, {
config: { wizard: { panelState: { fields: draft.fields } } }
});
if (draft.fields.length > 0 && draft.nodes.length > 0) {
const replaceOps: BatchOperation[] = draft.nodes.map(n =>
wizardApi.ops.createView({ display_name: n.display_name, fields: draft.fields })
);
const res = await wizardApi.applyBatch(draft.flowKey, replaceOps, actor);
const newViewKeys = (res?.result?.ops || []).map((o: any) => o.key);
const wireOps: BatchOperation[] = draft.nodes.map((n, i) =>
wizardApi.ops.presentationEdge(n._key, newViewKeys[i])
);
if (wireOps.length) await wizardApi.applyBatch(draft.flowKey, wireOps, actor);
updateDraft({ nodes: draft.nodes.map((n, i) => ({ ...n, viewKey: newViewKeys[i] })) });
}
updateDraft({ step: "Validate" });
pushToast("ok", "Data requirements saved");
pushToast("ok", `Saved ${draft.fields.length} fields onto every step's form`);
} catch (err: any) {
pushToast("err", `Save failed: ${err.message}`);
} finally {