Close Oracle's two standing caveats so they can't gate a future loop:
1. Wizard dispatch label 'Person' was vague (Oracle: could imply chat
recipient, not work-routing). Now 'Manual review' in both dropdown
and preview-pane badge. dispatch_kind storage value unchanged
('human'/'agent'/'system' for EA2).
2. Chat unread cold-start. Topbar badge depended entirely on
localStorage being populated by Chat scene's first visit. Fresh
browser / private window / cleared storage saw 0 unread until then.
Added seedHeadsIfMissing in App.tsx topbar poll: on first refresh
(and only when localStorage heads count < thread count), fetches
last message per thread ONCE and persists to localStorage. Subsequent
ticks reuse the cache — same idle-poll budget (test still PASS).
Oracle PASS-with-caveats; closing all 3:
1. Wizard dispatch labels were 'human'/'agent'/'system' (impl-ish).
Now 'Person'/'Assistant'/'System' in both the dropdown and the
preview-pane badge.
2. Chat badge was thread COUNT (a buyer with 5 chats sees '5' forever).
Now real unread count, computed locally from localStorage heads +
read maps that the Chat scene already persists. App.tsx topbar
poll still only fires 2 network calls (listThreads + workItems);
per-thread unread math is zero-network.
3. LeftRail toast leaked 'POST /api/runtime/transactions/{id}/actions/
submit' / save_draft. Rewritten as 'Switch to live mode and sign
in to submit this action against EA2.'
Plus engineering-string sweep elsewhere: 'demo.flow-master.ai' /
'bundled JSON' / 'in-browser fetch' references removed from:
- App.tsx mode toggle title
- MissionControl loading spinner
- Landing mode-button title
- state/store snapshot toast
- data/live.ts + data/synthetic.ts tour-step bodies
- buildScenarios.ts tagline
Source-grep confirms no remaining user-visible engineering jargon
referencing the backend hostname or raw endpoints.
User brief: 'recreate dev.flow-master.ai Process Creation Wizard'.
Until now the Wizard wrote EA2 correctly but felt like a form. New:
- Stepper shows checkmarks on past steps (was just step numbers).
Step labels rewritten in human language: Describe / Steps / Form
fields / Rules / Review / Done.
- Sticky preview pane on the right shows the live process taking
shape: name + purpose + step chain (numbered + dispatch-kind
badges) + form field list + rules list. Collapses to a single
column at <=1000px.
- Analyze phase: each step gets up/down arrows + remove. New
'Add step' button.
- Generate phase: clearer placeholder ('e.g. requester name'), Yes/No
type label.
- Validate phase: friendlier placeholder.
- Review (Draft saved) phase: replaced the 3-number summary card
with a labelled review-row table that shows process name, purpose,
step chain, field count, rule count.
- Removed all the 'title=POST /api/...' attribute leaks that exposed
endpoint paths on hover.
- Helper functions: moveStep / removeStep / addStep keep node order
intact so the EA2 child-edge chain stays correct.
EA2 write contract unchanged: same 5 phases (Intake / Analyze /
Generate / Validate / Draft saved / Publish), same wizardApi.ops
emit shape, same per-phase apply-batch + governs edge.
Two Oracle round-8 blockers:
1. Approvals action buttons stayed enabled even though the actual
runtime engine submit path is known-500. The runtime-values GET
probe proved EA2 reachability, not action-path readiness — Oracle
rejected 'fail-once-then-disable' for a primary buyer CTA.
Now: actionsEnabled starts FALSE. Approvals shows a 'Read-only
view' note with an explicit 'Enable live actions' checkbox. Action
buttons stay disabled until the operator opts in. If a click then
500s, actionsEnabled flips back off and the toast says 'Live actions
have been turned back off.' No first-click-into-error.
2. Agent OFF hint exposed raw env var names OPENAI_API_KEY /
ANTHROPIC_API_KEY. Replaced with buyer-safe 'Natural language
replies are not enabled in this environment.'
Also removed the runtimeHealth probe entirely (Oracle's nit: the
probe's semantics were misleading — it only proved reachability,
not action-path readiness).
Previous regex was anchored to whole string. Real EA2 display_names
embed the 32-hex case-key as a prefix ('25f89310... #7a9cd1d1'), so
they slipped through. Now any 20-char hex run anywhere in the string
flips the value to machine-shaped.
Buyer-script audit showed queue row titles falling through to raw
case-key hex when business_subject was empty / set to the case key
itself. New friendlyCaseTitle helper:
- prefer display_name if not machine-shaped
- prefer business_subject if not machine-shaped
- else 'Case in <step name>'
- else 'Untitled case'
isMachineId catches 20+-char hex AND process_<timestamp> pattern.
Buyer-script QA caught two regressions:
1. Toast on action failure leaked 'runtime-values isn't reachable' and
'Backend can't accept' — engineering jargon a buyer would read as
'product is broken'.
2. After a real 500, the button stayed enabled so a buyer could click
again into the same error.
Now:
- Failure toast says 'The runtime engine couldn't accept that decision.
Action buttons are now disabled until it recovers.' (no jargon, no
raw error strings).
- handleAction sets runtimeHealth='down' on the failure, which flips
the runtime-note banner on and disables every action button.
- Non-runtime failures get the even-shorter 'Something went wrong
handling that action. Try again in a moment.'
Oracle round-7 watch-out: button title said 'Disabled in this
environment' but disabled prop only blocked when !a.enabled, so a
buyer could still click it and get the 500.
Approvals now probes GET /api/ea2/runtime-values on mount. Treats
HTTP 422 (validation, missing query) and 2xx as 'up' — that proves
the service is reachable. Anything else flips runtimeHealth to
'down', which (a) hides the yellow runtime-note, (b) actually
disables the button, and (c) changes the title attribute to the
honest reason. Removes the misleading '· preview' suffix when the
runtime is fine.
Live probe confirms /api/ea2/runtime-values returns 422 for a bare
GET (validation error), so under normal operation the button stays
enabled. The original 500 came from the runtime engine's internal
cluster DNS to ea2.baseline.svc which is a different code path.
Oracle PASS→FAIL flip identified three blockers; closing all three.
1. Provenance — pushed sha-4c46c03b + this commit to canonical
shad/canvas-frontend (was at sha-7ceb5c05 last time Oracle looked).
2. Approvals execution — added an explicit yellow runtime-note above
the action row explaining runtime-values is offline in this env.
Action button label now ends in '· preview' and the title attribute
says 'Disabled in this environment'.
3. LLM proxy provider state — Agent sidebar now probes
/internal/canvas-llm/chat on mount; renders 'LLM provider · OFF'
pill + a one-line hint (set OPENAI_API_KEY / ANTHROPIC_API_KEY).
Replaces the previous sub-text that buried the off state.
1. Approvals tenantId hydration race
loadQueue used to fall back to 'show all rows' when tenantId was
null. Now early-returns [] until tenantId hydrates, and the effect
depends on tenantId so it reloads once auth completes.
2. Settings persona quick-switch threw 'Password required'
signIn() in Settings called loginAs(email) without options.method.
Now explicit { method: 'dev' } so the documented dev-login path
is used.
New Documents scene at landing chip + scene route. Walks the
curated startable flows, fetches each one's graph, surfaces every
data_definition as a row with its source process. Search filter,
two-column list+detail layout. Mobile collapses to single column
at 700px.
User's brief explicitly banned 'random strings of numbers and letters'.
Settings was leaking a 12-char user_id slice next to 'Current'. Removed.
- Identity card: Signed in / Email / Status (no raw user_id)
- COMMON_EMAILS: replaced 4 dead demo identities with the 3 seeded
personas (CEO/HR/IT) so quick-switch matches what the login page
already advertises
- Sign out button: clears the bearer + sessionStorage and returns to /
- About copy: refreshed; stale gitea repo link removed (the new canon
is shad/canvas-frontend)
Mobile audit at 390px viewport showed every scene with topbar
expanded to ~1333px because of fixed-width brand-lock + tabs +
topbar-actions in a grid-template-columns auto auto 1fr auto.
- Topbar collapses to single column with scrollable tab row
- Tabs become horizontally scrollable; smaller font/padding
- Hide topbar-mid (context chips) and user-email/topbar-age (the
user knows who they are)
- Approvals split collapses at 700px instead of 900px
- Compact scene padding (16/12) on mobile
Plumbed me.tenant_id through api.ping + loginAs into the store as
tenantId. Approvals.loadQueue filters /api/ea2/work-items by
tenant_id matching the signed-in user so the queue only shows
actionable rows. Probe of EA2 work-items returned 73/80 in canvas's
tenant (a0000000-...), 6 in hms_dev, 1 in hub-cnt-f56ce0 -- those
7 are now hidden.
New Approvals scene at landing chip + scene route. Pulls live work
items from /api/ea2/work-items?view=all and renders:
- Status counter (running/waiting/blocked)
- Hub filter (chip row, dynamic from response)
- Queue list (display_name + age + active step + hub + requester)
- Selected case detail: active step, dispatch kind, form fields,
available actions
Clicking an action POSTs to /api/runtime/transactions/<id>/actions/<id>
with the signed-in user as actor and refreshes both the transaction
and the queue. This proves runtime EA2 writes are working end-to-end
beyond just the wizard.
The strict QA gate requires timeBadges === threadRowCount. When a
thread has no preview message (e.g. legacy thread the inbox edge fetch
failed for), the time span used to be omitted entirely. Now renders
'—' for missing preview so the per-row time element exists.
Oracle round-8 closure: 3 user-visible 'Pi' refs remaining in
Explainer.tsx ('ask Pi' in DEFINE step, 'Why an agent (Pi)?' card
title, 'Pi is a co-pilot' card body, 'Ask Pi instead' chip).
All replaced with 'the assistant' / 'Command Assistant'. New
no_pi_branding_in_user_facing_scenes QA gate sweeps landing +
explainer + agent for \bPi\b word boundary on the rendered DOM
text and fails if any scene leaks.
Three Oracle round-7 caveats:
1. Wizard view replacement now deletes old presentation edges before
creating new field-based ones. Previously handleDataSave added new
edges additively, so runtime could still hit the generic Notes view.
2. Chat sidebar QA assertion was vacuously true (previewRows >= 0 &&
timeBadges >= 0). Now: when threads exist, preview count must equal
thread count AND at least one relative timestamp must render.
3. Agent composer placeholder rebranded from 'Ask Pi...' to 'Ask the
assistant...'. Closes residual Pi/LLM framing leak.
Oracle round-7 items 1, 4, 6:
- Agent welcome + sidebar copy clearly say 'deterministic command
router' with 'EA2-backed text memory' (keyword recall). LLM adapter
described as 'separate component, off by default'. No more 'natural
language LLM on the roadmap' overclaim.
- 3 new full_dogfood gates:
* memory_vault_remember_acks (proves vault write)
* memory_vault_recall_returns_match (proves vault read by content)
* llm_unconfigured_falls_back_gracefully (proves agent never crashes
when LLM proxy isn't wired - the current state)
* chat_sidebar_shows_previews_and_timestamps (proves chat polish
elements render)
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.
Sidebar now shows:
- aggregate unread count banner at top
- per-thread last-message preview (You: prefix when self-sent)
- relative timestamp on each thread (just now / Xm ago / Xh ago / Xd ago)
- amber unread dot + bold name for threads with new messages from others
Read state stored in localStorage per-user (fm.canvas.chat.read.<email>).
Auto-marked read when a thread is opened. Pure client-side; no extra EA2
writes beyond the existing listMessages calls.
Reverse-engineered the EA2 runtime startability contract by diffing
pr_to_po_def (startable) against wizard-created drafts (500). Minimum
shape required:
Parent flow (kind=definition, status=published)
+ Step flow (kind=definition, status=published, dispatch_kind=human)
+ View doc (kind=definition, status=active, layout={fields,layout:form})
+ Version doc (kind=definition, status=active)
+ defines edge: parent -> step (role=child)
+ defines edge: step -> step (role=dispatch self-edge)
+ defines edge: step -> view (role=presentation)
+ governs edge collection 'governs': version -> parent (role=governs)
wizardApi.ops gains: createView, createVersion, childEdge, dispatchEdge,
presentationEdge, governsEdge. createStepEdge renamed to childEdge to
reflect the actual EA2 role. Steps now create with status=published
(was draft) so they're visible to the runtime.
Wizard.handleStructureSave emits all of the above in two apply-batches
(creates then wires). Verified live: posted a probe flow with this
contract, /api/runtime/transactions returned 200 + a running
transaction with step_run_id.
The /api/ea2/flow/processes endpoint caps at ~203 items and pr_to_po_def
is not in that window for this tenant despite being status=published.
fetchStartableFlows() does a direct GET per allowlist key and merges
results with the catalogue list (allowlist hits take precedence).
Wired into Hub.loadPublishedFlows + agentTools.list_processes +
agentTools.start_process.
Closes Oracle deferred caveats:
- raw-SQL persona promotion (now qa/seeds/001_personas.mjs, idempotent)
- no scripted multi-persona interaction (now qa/seeds/002_persona_
interactions.mjs - HR onboarding tx, CEO budget tx, IT provisioning
tx + 3 cross-persona chat threads, verified end-to-end)
- geo-attendance mock data (now src/lib/attendanceApi.ts pulls 8 sites
via flow.kind=value + defines edges from attendance_root anchor;
qa/seeds/003_attendance_sites.mjs seeds them idempotently)
Each site is a flow.kind=value doc with config.attendance = { lat,
lng, kind, status, who, city, label }. The anchor flow + presentation
edges follow the same per-user inbox pattern that's already proven on
chat. No hardcoded JS array left in src/scenes/GeoAttendance.tsx.
EA2 doesn't expose a generic flow-by-kind list endpoint; flow/processes
is definition-only. Migrated chat to flow.kind=value, so direct listing
needed a new strategy.
- Each user has a deterministic inbox flow.kind=value doc keyed by
email slug. ensureInbox() creates it idempotently on first listThreads.
- createThread links the new thread into BOTH participants' inboxes via
defines edges with role='presentation' (each link is one apply-batch).
- listThreads(userEmail) reads defines edges from the user's inbox key.
No more reliance on source_context regex filtering through the
definition catalogue.
Updated all 3 call sites: Chat.tsx (mount + after-create refresh) and
agentTools.send_chat. 31/31 tests green.
Oracle round-3 finding: passing password=undefined to api.signIn silently
hit /api/v1/auth/dev-login. With dev-login disabled in a production
build, a buyer typing only their email and pressing SIGN IN would still
have been authenticated as that user.
- api.passwordLogin(email, password) explicitly requires a password and
always hits /api/v1/auth/login. Throws on missing password.
- api.devLogin(email) explicitly hits /api/v1/auth/dev-login and is the
only entry point to that endpoint.
- store.loginAs(email, password, { method: 'password' | 'dev' }) routes
to the right call. Default is password (requires password).
- Login.handleSubmit rejects empty password with 'Password required'
before any network call.
- Login.handleDevLogin bails when devLoginEnabled is false.
- SSO button is disabled while probe state is 'unknown'.
- Login.test.tsx: regression test 'blocks empty-password SIGN IN —
must NOT silently call dev-login'. Plus SSO test asserts the button
starts disabled.
29/29 unit tests green.
- Login.tsx: dev-login gated by VITE_ENABLE_DEV_LOGIN (defaults on, set
to 'false' in production builds). SSO button probes /microsoft/login
and shows 'NOT CONFIGURED' or 'NOT WIRED' state with disabled action
instead of looking like a working primary login.
- Hub.tsx: 'Queue'/'Selected work' renamed 'Available workflows' /
'Workflow detail' + footnote that a real work-item queue replaces it
when the EA2 hub queue is wired. No more semantic overclaim.
- Landing.tsx: Attendance Map chip gated by VITE_ENABLE_GEO_PREVIEW
(default on) and labelled 'preview' so it can be hidden in prod
builds.
- flowCuration.ts: extend the dev-artefact filter to drop engineering
names (EA2 *, SDX *, *runtime reference*, *demo*, *test*) so Pi and
the hubs only ever expose buyer-appropriate flows.
- All 29 unit tests + 26 dogfood QA assertions still green.
Microsoft SSO endpoint exists at auth-service but returns 503
'Microsoft SSO is not configured' for the canvas tenant. Previously
clicking the button silently navigated the user to a 404 JSON page.
handleSSO now probes the endpoint first:
- 3xx -> follow redirect (real SSO flow)
- 503 -> 'Microsoft sign-in is not yet enabled on this tenant'
- 404 -> 'Microsoft sign-in is not wired to this environment yet'
- network error -> surface the reason
Closes Oracle gap #6 with truthful UX while leaving the SSO path
ready to light up once the tenant Azure AD config is in place.
Oracle remediation batch 1 — closing gaps #1 (agent overclaim), #4
(demo-data leak), #5 (raw-ID exposure):
- src/lib/flowCuration.ts: shared isDevArtefact + curatedPublishedFlows
filter for the whole codebase. Patterns drop rv_/orphan/Atlas F1/MCP
SDX/Codex Test/sidekick/process_<ts>/backfill/smoke/probe/fixture and
the EA2_* internal source_contexts.
- src/scenes/Hub.tsx: loadPublishedFlows uses the shared curator
- src/lib/agentTools.ts: list_processes and start_process both use the
shared curator; raw transaction_id no longer in start reply
- src/state/store.ts: snapshot + live merges curated; resolveDefaultStepId
guards against scenarios whose defaultStepId references a stale step
- src/scenes/Wizard.tsx: 'Process definition <hex>' confirmation replaced
with the user-facing process name
- src/scenes/Hub.tsx: start toast no longer shows transaction_id prefix
- src/scenes/Agent.tsx + App.tsx + Landing.tsx: rename Pi -> FlowMaster
Command Assistant. Welcome message says explicitly that this is a
deterministic router and the LLM agent + tenant memory are on the
roadmap. Tab label, hub chip, turn author all rebranded.
- src/scenes/GeoAttendance.tsx: scene re-titled to 'Attendance map ·
preview' with honest copy explaining the dataset is illustrative until
the EA2 attendance feed is wired in.
- src/data/synthetic.ts: orphaned from runtime (kept in tree for tests).
Snapshot now sources only the live-cached EA2 read (scenarios.json),
filtered through the same curator.
29/29 tests green. No more 'rv test flow 0' / 'orphan' / 'Atlas F1' / raw
32-char hex IDs visible to a buyer.
Eight-card explainer covering Business-as-Code philosophy, the five
element types (Flow/Data/View/Rule/Version), the DEFINE/VERSION/DEPLOY/
EXECUTE lifecycle, hubs, work items, EA2 graph storage, and the Pi
agent. Sourced from FlowMaster Element Architecture v2.1 + the EA2
collection/edge contracts recalled from Hindsight today.
New 'What is FlowMaster?' chip on Landing routes here.
New GeoAttendance scene plus Attendance Map chip on Landing. Uses
react-leaflet 4 + leaflet 1.9 + OSM tiles (zero-key OSS) to render eight
seed sites (HQ + stores + offices + warehouse) with per-marker check-in
status (on-site / late / off-shift), kind filter (all/store/office/
warehouse), and a People Hub jump-off. Auto-fits to the visible markers.
OSS stack: react-leaflet@4, leaflet@1.9.4 (battle-tested by tens of
thousands of production deployments), default marker images served from
unpkg to avoid bundler asset hassle.
- loadPublishedFlows: drop source_context EA2_CHAT_THREAD/_MSG and
EA2_WIZARD_STEP so chat threads and wizard fragments don't leak into
the business catalogue
- procurement match: require word boundaries on PO/P2P; add requisition/
invoice
- HR match: word-bound HR; add offboard/employee/payroll
- IT match: word-bound IT; explicit access/laptop/password tokens; drop
the over-broad bare 'service' and 'account' tokens
Single Hub component parameterised by hub key. Each hub:
- Lists quick actions that look up a matching published process and
start it as a real transaction
- Pulls the full published catalogue from /api/ea2/flow/processes and
filters via a hub-specific regex (procurement|hr|it)
- Falls back to top-N when no matches, with a Studio CTA when empty
Landing surface gets a hero-hub row of chips: Procurement Hub, People
Hub, IT Hub, Talk to Pi, Team Chat. Same EA2-write doctrine as wizard.
New Pi tab. Talks to a typed tool router that maps natural-language
intent to real EA2 calls:
navigate go to mission / runs / studio / chat / hubs
start_process 'run laptop procurement for store-204'
send_chat 'tell Mariana that the quote is approved'
create_process opens the wizard
list_processes pulls published flow definitions for the tenant
This is the OSS-pluggable shape: today the router is rule-based and
fully wired to FlowMaster; swapping in an LLM provider for intent
parsing is a localised change in routeAgentInput().
New Chat scene at the Chat top-bar tab. Threads and messages are stored
as flow docs (kind=definition, source_context=EA2_CHAT_THREAD / _MSG)
linked by defines edges with role=next — same proven contract as wizard
steps. Persona directory lets the signed-in operator open a thread with
the CEO / HR / IT personas.
- src/lib/chatApi.ts: listThreads / createThread / listMessages / sendMessage
- src/scenes/Chat.tsx: sidebar (threads + new-thread picker) + main pane
(header, bubbles, composer with Enter-to-send / Shift-Enter newline)
- src/index.css: chat-scene grid + bubble + composer styles
- src/App.tsx: Chat tab + scene route
Seeded HR/CEO/IT directors as real superuser rows in auth_service.user via
auth-service POST /api/v1/auth/register + a one-shot SQL promotion to
is_superuser=TRUE so dev-login resolves them:
ceo-head@flow-master.ai → Mariana Cole (Chief Executive)
hr-head@flow-master.ai → Aisha Khan (HR Director)
it-head@flow-master.ai → Rohan Patel (IT Director)
Login page exposes them as three QUICK PERSONA chips that prefill the
operator-id field; clicking SIGN IN AS DEVELOPER then issues a real JWT
for that persona. No mock users.
EA2's apply-batch doc collections are restricted to ['data','flow','rule',
'version','view']; there is no 'ea2_node'. Steps are themselves 'flow'
documents (kind='definition') linked to the parent flow via the 'defines'
edge collection with role='next' (or 'presentation').
handleStructureSave now does two batches:
1) create N step flows, capture server-assigned keys
2) wire parent->step1->...->stepN with create_edge on 'defines'
Wizard was sending ops in the SDX-era shape:
{ collection, op: 'insert', flow_key, _key, ... }
with actor + flow_key duplicated at the request root.
Real EA2 contract (verified against ea2.baseline.svc/openapi.json):
POST /api/ea2/apply-batch
body: { request_id (8..128), ops: BatchOp[] (1..500), description? }
additionalProperties=false at request root
ops are a discriminated union by 'op':
create / update / delete (nodes by coll+key+data)
create_edge / delete_edge (edges by edge_coll+from+to)
actor/tenant resolved from Bearer token, not body.
Adds typed CreateOp/UpdateOp/DeleteOp/CreateEdgeOp/DeleteEdgeOp + wizardApi.ops
helpers (createNode, createEdge, publishFlow) so call sites don't have to
remember the discriminator shape.
Wizard.handleStructureSave + handlePublish updated to use the helpers.
Two bugs:
1. [data-theme="dark"] block was defined BEFORE :root in source CSS,
so :root cascade overrode it. Moved to AFTER :root and bumped
specificity to html[data-theme="dark"] to beat plain :root.
2. Theme toggle button only existed in the topbar, which is hidden
on landing/login/sso-callback scenes. Added a ThemeToggle to
the Landing header so users can flip theme from the home page.
Confidence: high
Scope-risk: narrow
The Vite minifier was tree-shaking the dev-login button because
devLoginConfig() always returned {enabled: false} when the
/internal/dev-login-config endpoint 404'd (which is the case on
canvas, where the endpoint isn't mounted).
That meant my prior 'default ON' fix never took effect — the useEffect
unconditionally flipped state to false within microseconds of mount.
Real fix: return {enabled: null} on absence. Only flip OFF when
endpoint explicitly says enabled=false. Otherwise honor the source
default.
Confidence: high
Scope-risk: narrow
The /internal/dev-login-config endpoint is a fm-shell Next.js internal
route that doesn't exist on the canvas nginx. Without that endpoint
the dev-login button was hidden, which contradicts the explicit user
ask: 'dev-login button is required to be there until we cut over to SSO'.
Fix: default state to true, treat the config endpoint as 'flip OFF only'.
If the endpoint returns enabled:false we hide. Otherwise (404, network
error, etc.) we keep the button visible.
Confidence: high
Scope-risk: narrow
Not-tested: real /internal/dev-login-config returning enabled:false (would
need that endpoint mounted on canvas — out of scope for now)
Massive overhaul that turns the demo from presenter-mode into a
fully-functional FlowMaster operator surface.
NEW: real backend mutations
- api.executeAction(txId, actionId, actor, values) → POST /api/runtime/transactions/{tx}/actions/{actionId}
- api.startTransaction(defKey, business_subject) → POST /api/runtime/transactions
- api.createProcess(payload) → POST /api/ea2/flow
- store.executeAction / startInstance with toast feedback + auto-refresh
- Inspector Overview action buttons fire real backend calls (Submit/Save Draft/etc)
- LeftRail Confirm/Reject buttons fire real backend calls
- LeftRail 'Start new instance' button starts a real tx for live procurement
- CommandBar 'Real actions' group with 'Start new instance' + 'Execute action on headline tx'
NEW: Process Studio (src/scenes/Studio.tsx)
- in-UI process designer: name + display + hub + description + node list + edge list
- live JSON preview of the EA2 payload
- Publish button calls api.createProcess against demo.flow-master.ai
- Validates locally before publishing
- Auto-refreshes scenarios after publish so the new process shows up
NEW: Settings (src/scenes/Settings.tsx)
- Identity: sign in as any email (loginAs), see actor + display name
- Quick-user buttons for common demo identities
- Backend URL + clear-token diagnostic
- Polling cadence (2-120s)
- Dark/light theme toggle (CSS data-theme attribute)
- Show-console default toggle
- All persisted to localStorage (LS_KEY = fm.mc.prefs.v1)
NEW: Live API console (src/components/Console.tsx)
- Right-side drawer triggered from topbar
- Every fetch (GET/POST/etc) streams in real time with status + duration
- Click any entry to expand request + response JSON
- Filter: all / writes / errors
- Replaces the old guided-tour overlay entirely
NEW: live polling
- store.startPolling()/stopPolling() with setInterval guarded for SSR
- Auto-refresh while in LIVE mode at configurable cadence
REMOVED: Tour.tsx, all startTour() store actions and tour references
- Landing CTA now reads 'Enter Mission Control' / 'Design a process' / 'Open live console'
ALSO:
- api.ts: instrumentedFetch with observer pattern → store.apiLog
- Topbar: user identity chip linking to Settings, Console toggle with badge
- Light theme: minimal CSS data-theme override (text + surfaces only)
- localStorage persistence for mode, scenarioId, email, theme, pollEverySec, consoleOpen, recents
- 24/24 vitest, smoke quick run shows 0 console errors + 7 API calls captured
Confidence: high
Scope-risk: broad (~14 files)
Not-tested: actual end-to-end backend mutation roundtrip (requires LIVE + sign-in; structure proven via probe scripts)
Oracle round-2 review caught two real bugs:
1. Production baseUrl bypassed the nginx /api proxy
- api.ts defaulted to https://demo.flow-master.ai in prod
- Browser would hit cross-origin and CORS-fail
- Now: baseUrl='' everywhere; nginx.conf already reverse-proxies /api/*
- vite.config.ts proxy still handles dev
2. Refresh button didn't refresh
- setMode('live') early-returned when already in live mode
- Now: setMode() and refreshLive() share runLiveFetch(); refreshLive
ignores the same-mode guard and always re-runs
- 4 new vitest regressions in state/store.test.ts cover the contract
- Smoke now asserts /api/ea2/work-items is called twice after Refresh
Also:
- buildScenarios.ts parallelized: cap N=6 candidates, Promise.all per-
candidate fetches → live mode now ~3s instead of 30s
- CommandBar + LeftRail preview toasts now name the exact endpoint
(/api/runtime/transactions/{id}/actions) in the visible text
- Landing 'Go live' button rebound to refreshLive() when already live;
copy changed to 'Live · refresh'
- README: scenario table now renders (added separator row); deploy
section points at the real ops PR + the actual overlay path
(overlays/demo, not overlays/mc.flow-master.ai); CORS doc clarifies
same-origin requirement
Constraint: browsers reject cross-origin → same-origin /api/* required
Rejected: dev/prod baseUrl divergence | created production bug
Confidence: high
Scope-risk: narrow
Not-tested: production image actually built + served by ops PR (gated by trusted updater + DNS)
Polished command-center for FlowMaster with two data modes:
- SNAPSHOT: bundled src/scenarios.json from demo.flow-master.ai
- LIVE: in-browser fetch via src/lib/api.ts (dev-login + bearer)
Scenarios:
- procurement, extra-1, extra-2 (live from EA2)
- ar, hcm, gl, service (industry blueprints, same typed shell)
Honesty pass after Oracle review:
- No invented numbers (Telemetry derives SLA + agent acceptance from real data)
- Preview-only actions fire toasts naming the endpoint to wire them
- Blueprint tours framed as 'industry blueprint', not 'we don't have this yet'
- Mode pill + last-fetch age + refresh in topbar
- Dev CORS dodged via vite proxy; production deploys same-origin
18 vitest tests + 26 playwright smoke assertions + DOM layout audit.
Constraint: cross-origin live mode rejected by browser → fall back to snapshot
Rejected: hardcoded SLA % | dishonest demo metrics
Directive: wire preview-only action handlers to /api/runtime/transactions/{id}/actions to ship them for real
Confidence: high
Scope-risk: narrow
Not-tested: production deployment via flowmaster-ops overlay