Files
canvas-frontend/src/lib/flowCuration.ts
T
shad cc037a149e 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.
2026-06-14 15:07:57 +04:00

95 lines
2.6 KiB
TypeScript

export interface RawFlow {
_key: string;
display_name?: string;
name?: string;
description?: string;
source_context?: string;
status?: string;
kind?: string;
}
const DEV_ARTEFACT_PATTERNS = [
/^rv[_\s]/i,
/^orphan/i,
/^atlas[_\s]?f1/i,
/\batlas-f1-fresh\b/i,
/mcp[_\s]?sdx[_\s]?smoke/i,
/\bcodex[_\s]?test\b/i,
/\bsidekick\b/i,
/^process_\d{10,}$/i,
/\bbackfill\b/i,
/\bprobe\b/i,
/\bfixture\b/i,
];
const NON_BUSINESS_SOURCE_CONTEXTS = new Set([
"EA2_CHAT_THREAD",
"EA2_CHAT_MSG",
"CANVAS_CHAT_THREAD",
"CANVAS_CHAT_MSG",
"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",
]);
export const STARTABLE_FLOW_KEYS = new Set<string>([
"pr_to_po_def",
]);
const STARTABLE_SOURCE_CONTEXTS = new Set<string>([
"EA2_DRAFT_PROCESS:process_creation",
"fm06-t10-demo-reset-v2",
]);
const HEX32 = /[a-f0-9]{32}/gi;
const HEX_SUFFIX = /[_-][a-f0-9]{8,}$/i;
export function isDevArtefact(f: RawFlow): boolean {
if (f.source_context && NON_BUSINESS_SOURCE_CONTEXTS.has(f.source_context)) return true;
const haystack = `${f.display_name || ""} ${f.name || ""} ${f.description || ""}`;
return DEV_ARTEFACT_PATTERNS.some((p) => p.test(haystack));
}
export function curatedPublishedFlows<T extends RawFlow>(items: T[]): T[] {
return items.filter(
(it) =>
it.status === "published" &&
it.kind === "definition" &&
!!it.display_name &&
!isDevArtefact(it) &&
(STARTABLE_FLOW_KEYS.has(it._key) || (it.source_context && STARTABLE_SOURCE_CONTEXTS.has(it.source_context)))
);
}
export async function fetchStartableFlows(baseUrl: string, token: string): Promise<RawFlow[]> {
const results = await Promise.all(
Array.from(STARTABLE_FLOW_KEYS).map(async (key) => {
try {
const r = await fetch(`${baseUrl}/api/ea2/flow/${key}`, { headers: { Authorization: `Bearer ${token}` } });
if (!r.ok) return null;
const doc = await r.json();
if (doc.kind !== "definition" || doc.status !== "published" || !doc.display_name) return null;
return doc as RawFlow;
} catch { return null; }
})
);
return results.filter((d): d is RawFlow => !!d);
}
export function friendlyShortId(id: string | undefined | null): string {
if (!id) return "";
const trimmed = id.replace(HEX_SUFFIX, "");
if (trimmed.length < id.length && trimmed.length > 0) return trimmed;
if (HEX32.test(id)) return `ref-${id.slice(0, 4)}`;
return id;
}
export function scrubIdsFromText(text: string): string {
return text.replace(HEX32, "");
}