Commit Graph
36 Commits
Author SHA1 Message Date
shad 300024964f fix(login): persona click signs in directly + friendly error copy (#3)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
2026-06-14 21:26:01 +00:00
shad a02cc70e68 fix(oracle-l8): close 3 polish caveats (dispatch labels / chat unread / leftrail leak)
build-and-publish / image (push) Has been cancelled
build-and-publish / test (push) Has been cancelled
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.
2026-06-14 19:08:44 +04:00
shad 071166cae1 feat(approvals): tenant-scoped queue (no cross-tenant work-items)
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.
2026-06-14 16:01:54 +04:00
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
shad 15ab8ededa feat(agent): LLM proxy integration shape (Pi-AI compatible)
src/lib/llmClient.ts: browser POSTs to /internal/canvas-llm/chat with
{messages, max_tokens}. Mirrors the normalized request shape used by
earendil-works/pi's @earendil-works/pi-ai. Returns:
 - ok:true with content+provider when configured and successful
 - ok:false, configured:false when proxy 404/503/unreachable
 - ok:false, configured:true when provider returned an error

routeAgentInput now falls back to the LLM with a system prompt that
includes the user identity, tool catalogue, and the top-3 vault recall
hits when no deterministic tool matches. When the proxy is not
configured the deterministic vault-hint reply is used (no regression
in the demo tier).
2026-06-14 14:55:34 +04:00
shad 9884ddf4ac feat(agent): per-tenant Karpathy-style memory vault
User explicit ask: 'maybe with a miniature hindsight or a Karpathy
vault behind it as its memory system for each tenant.'

- src/lib/agentMemory.ts: EA2-backed vault per user. Vault root is
  flow.kind=value at flow/memory_vault_<email_slug>; each memory is a
  flow.kind=value doc linked via defines edge with role=presentation.
- agentMemory.remember/listAll/recall — recall is a simple keyword
  overlap score with recency tiebreak.
- agentTools: new 'remember' and 'recall' tools.
  matchers: 'remember that ...', 'note ...', 'save ...' /
            'recall ...', 'remind me ...', 'what do you know about ...'
- routeAgentInput: every user turn is best-effort persisted as an
  episodic memory (fire-and-forget). When no command matches, the
  router does a vault recall and surfaces related notes before
  showing the help message.

Vault key: deterministic per-user. Owner_email stored on the vault
root + on each memory's config.memory. No raw IDs visible to the user.
2026-06-14 14:52:01 +04:00
shad 221cccf96b feat(curation): allowlist wizard source_context so user-created flows surface
EA2_DRAFT_PROCESS:process_creation and fm06-t10-demo-reset-v2 added to
STARTABLE_SOURCE_CONTEXTS. Wizard-published flows now reach the Hub
catalogue and the Assistant's list_processes reply.
2026-06-14 14:47:25 +04:00
shad 338864a8e1 fix(wizard): unique names on createStep/View/Version to avoid (tenant_id,name,kind) collisions 2026-06-14 14:44:51 +04:00
shad b951724c5f feat(wizard): produce fully startable EA2 flows
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.
2026-06-14 14:42:55 +04:00
shad c779919453 feat(curation): fetchStartableFlows supplements catalogue listing
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.
2026-06-14 14:32:50 +04:00
shad 83ebc28a81 fix(curation): drop over-broad demo/test/ea2/sdx/runtime-reference regexes
The allowlist by _key now handles those exclusions cleanly. The bare
'demo' / 'test' / 'ea2' regexes were sweeping out pr_to_po_def itself
('Executable curated procurement process for demo.flow-master.ai reset
smoke checks.') because its description contains both 'demo' and the
old smoke[_\\s]?test pattern variant.
2026-06-14 14:26:52 +04:00
shad e70944b6fa fix(curation): explicit STARTABLE_FLOW_KEYS allowlist
Previous attempt blacklisted unstartable backfill source_contexts but
swept all real published flows out too. Inverted to a positive
allowlist: only flows whose _key is in STARTABLE_FLOW_KEYS reach the
Hubs / Assistant.

Today the canonical pr_to_po_def is the only runtime-startable flow in
the tenant. New entries are added by hand once we've verified a flow
actually starts via /api/runtime/transactions. Cleaner than chasing
source_context strings.
2026-06-14 14:23:19 +04:00
shad 11e8442a93 fix(curation): blacklist source_contexts of un-startable backfill seeds
Oracle round-6 watch-out closure. The Assistant's list_processes was
returning PO Approval + Purchase Order Approval, both of which return
500 transaction_creation_failed on /api/runtime/transactions because
they were backfilled into the catalogue without view/data attachments.

Added 5 source_context exclusions matching the seed-time markers of
those flows: D2 live backfill / EA2 runtime reference seed /
atlas-f1-fresh-proof / EA2 seed payload via POST /api/process /
fm-process-mining-conformance-reseed.

The canonical pr_to_po_def (source_context fm06-t10-demo-reset-v2,
display 'Purchase Requisition to PO') survives — it is the only
runtime-startable flow in the tenant today.
2026-06-14 14:19:49 +04:00
shad 42884667f6 fix(curation): exclude seed-process + chat-inbox + attendance contexts + startability QA
Oracle round-6 watch-out: 'Employee Onboarding' (a CANVAS_SEED_PROCESS
wizard-incomplete draft) was reaching the Assistant list because its
display_name passed the dev-artefact regex. EA2 runtime then returned
500 transaction_creation_failed because it lacked view/data attachments.

- flowCuration.NON_BUSINESS_SOURCE_CONTEXTS now drops CANVAS_SEED_PROCESS,
  CANVAS_SEED_STEP, CANVAS_CHAT_INBOX, CANVAS_ATTENDANCE_ROOT,
  CANVAS_ATTENDANCE_SITE - all internal canvas source_contexts.
- qa/full_dogfood.mjs new assertion 'assistant_list_processes_all_startable'
  posts each Assistant-listed flow to /api/runtime/transactions and
  asserts non-4xx/5xx response.
2026-06-14 14:13:44 +04:00
shad b5d1ea54b0 feat(geo): EA2-backed attendance sites + versioned seed scripts
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.
2026-06-14 14:07:57 +04:00
shad 07eb93f67c fix(chat): create inbox via apply-batch (POST /api/ea2/flow rejects _key) 2026-06-14 13:53:21 +04:00
shad 7aaba5fb47 feat(chat): per-user inbox anchor for listing kind=value threads
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.
2026-06-14 13:51:53 +04:00
shad 8f35f7b88b feat(chat): migrate from flow.kind=definition hack to flow.kind=value
Oracle deferred caveat #2 — chat-on-flow-collection hack — CLOSED.

Previous design used flow.kind=definition + source_context=EA2_CHAT_THREAD
for chat threads and EA2_CHAT_MSG for messages, then filtered them out of
hub/wizard surfaces via a NON_BUSINESS_SOURCE_CONTEXTS exclusion. That
was containment, not a clean model.

Verified EA2 contract: flow.kind='value' is a valid enum value (probed
against ea2.baseline.svc — kind=value returns 200, kind=conversation/
thread/message/channel/note all return 400 schema violation).

- Thread: flow.kind=value, source_context=CANVAS_CHAT_THREAD
- Message: flow.kind=value, source_context=CANVAS_CHAT_MSG
- Linked via defines edges (already validated in round 2)

Because curatedPublishedFlows already requires kind='definition', chat
docs are now invisible to business surfaces by SCHEMA, not by source_
context regex filter. The filter is kept as defence-in-depth (+ legacy
docs created during round 2 still need to be filtered out).

Tried data collection first; it requires relates(role=sourced_from) edge
to an sdx_connections handle per the P19 invariant — wrong model for
chat, so flow.kind=value is the right home.
2026-06-14 13:46:19 +04:00
shad 1cfd787179 test(api): add 401-retry fail-closed regression
Oracle round-5 PASS verdict was non-blocking on this. Sibling test to
the no-token regression, proving authedRequest's 401 retry path also
throws AuthRequiredError instead of silently re-logging-in via
/api/v1/auth/dev-login when VITE_ENABLE_DEV_LOGIN=false.

31/31 unit tests green.
2026-06-14 13:42:29 +04:00
shad 5acdde3e27 fix(api): close second silent dev-login path in authedRequest
Oracle round-4 finding: authedRequest() called the private login()
helper whenever no bearer token existed AND on 401 retry. That helper
posts to /api/v1/auth/dev-login, so any authenticated API call could
silently issue dev-login regardless of UI gating.

- authedRequest checks devLoginAllowed() (VITE_ENABLE_DEV_LOGIN !==
  'false') before calling login(). When dev-login is disabled it
  throws AuthRequiredError instead.
- 401 retry path gated the same way.
- New AuthRequiredError exported so callers (store, scenes) can route
  unauthenticated users to the login page instead of swallowing.
- src/lib/api.test.ts: regression test 'throws AuthRequiredError
  instead of silently calling /dev-login when no token'. With
  VITE_ENABLE_DEV_LOGIN=false api.me() rejects with /Sign in required/
  and no POST to /dev-login is observed.

30/30 unit tests green.
2026-06-14 13:39:03 +04:00
shad b507ffe7e3 fix(login): split passwordLogin / devLogin so SIGN IN cannot bypass
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.
2026-06-14 13:34:54 +04:00
shad 456243c31c fix: gate unfinished surfaces honestly (oracle round 2)
- 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.
2026-06-14 13:27:59 +04:00
shad 8933148719 fix(curation): kill dev-artefact leak + rename agent honestly
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.
2026-06-14 13:11:54 +04:00
shad 47b1bf6998 feat(agent): Pi in-app agent with tool router
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().
2026-06-14 12:41:13 +04:00
shad 333667366d fix(chat): read messages via /api/ea2/edges/defines instead of /graph
process-definitions/{key}/graph filters to published process definitions
and returns empty for chat-thread flows, so the second persona saw no
message bodies. The raw defines edge endpoint returns the link correctly.
2026-06-14 12:37:33 +04:00
shad fd8c4853df feat(chat): EA2-backed 1:1 messaging surface
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
2026-06-14 12:33:43 +04:00
shad b7145c325b fix(wizard): use real EA2 flow/defines collections for steps
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'
2026-06-14 12:23:22 +04:00
shad 7b4f76c1de fix(wizard): drop data field from create_edge ops
EA2 server rejects data on create_edge with extra_forbidden despite the
OpenAPI schema marking it optional. flow_key is implicit from the node refs.
2026-06-14 12:19:06 +04:00
shad 82de744985 fix(wizard): match real EA2 apply-batch contract
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.
2026-06-14 12:14:09 +04:00
shad fab3b988b4 fix(wizard): include request_id in apply-batch payload
Backend EA2 apply-batch requires a request_id (idempotency key). Without
it, every node/edge write after Intake was failing with HTTP 422 and the
wizard stayed stuck on the Analyze phase.

Verified via qa/audit_wizard_writes.mjs against the live URL: previously
1 successful POST (draft creation) + 6x 422, now full multi-phase
write path enabled.
2026-06-14 12:08:26 +04:00
shad b26ea1ee9c fix(login): devLoginConfig() returns null when endpoint absent, not false
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
2026-06-14 11:35:15 +04:00
shad 4bc7ae228a feat: rebuild Process Studio into multi-phase Process Creation Wizard\n\n- Replace toy Studio.tsx with proper Wizard.tsx canvas\n- Build 6-step flow: Intake -> Analyze -> Generate -> Validate -> Draft saved -> Publish\n- Implement incremental EA2 writes via wizardApi.ts (flow, batch apply)\n- Use localStorage for draft recovery across reloads\n- Apply industrial-blueprint UI styling matching core doctrine\n- Add Vitest tests for Wizard components and API wrappers 2026-06-14 03:47:26 +04:00
shad dba1eb3328 feat(theme): promote industrial blueprint to the whole shell
Every scene now reads as the same FM doctrine: paper canvas + navy
frame + amber accent, 1px hairlines, square edges, monospace
uppercase labels, no glass, no shadows, no gradients.

WHAT CHANGED
- src/index.css rewritten end-to-end. Doctrinal hex tokens declared
  at :root, legacy --bg/--surface/--text/--primary/--border aliases
  repointed at doctrine values so existing component classes inherit
  the blueprint palette without per-component churn. Global
  * { border-radius: 0 } + box-shadow strip-out. All low-opacity
  tints via color-mix(in srgb, var(--bp-navy) X%, transparent) so no
  rgba() literals survive.
- Topbar redone as a 44px instrument strip: navy brand-lock with
  amber mark, uppercase mono tabs, amber-on-paper selected tab,
  square link buttons, mode pill with currentColor border.
- Mission Control hero + scenario tab strip + KPI cards retoken
  with amber underline on selected.
- Left rail: 1px-bordered KPI grid, queue cards stack as a single
  bordered list, agent supervision actions span the full width.
- Inspector: tabbed nav with amber selected, hairline-separated
  fields, square rule + run + evidence cards.
- Command palette: paper bg, amber-bordered selected item, mono
  caps headings.
- Live API console: paper drawer, mono call rows, amber filter chip,
  color-tokenised METHOD_COLOR map.
- Toaster: left-border accent on paper surface.
- Telemetry: navy gauges, mono row, square tick.
- Studio + Settings: shared studio-grid layout — paper panels in a
  1px navy grid with no margins between, mono inputs.
- Run History: mono table rows with amber selected filter chip.
- Landing: mono hero, square brand mark, stats strip as one
  bordered row, scenario cards in a 1px grid (no glow, no shadow,
  no gradient).
- Family accents in synthetic.ts and buildScenarios.ts retoken to
  doctrine hex. src/scenarios.json snapshot patched in place.
- Console.tsx METHOD_COLOR map → var(--bp-muted/info/amber/err).

AUDIT
- qa/palette_audit.mjs upgraded: scans 31 source files (.ts/.tsx/
  .json + index.css), catches rgb()/rgba()/hsl()/hsla() literals,
  and refuses named CSS colors (white/red/blue/...) as background/
  color/fill/stroke/border values. Hex regex uses (?![0-9a-fA-F])
  lookahead so deploy-id text like '#d3f1a' is not a false positive.
  Result: 0 non-doctrinal literals anywhere in src/.
- qa/smoke.mjs + qa/smoke_blueprint.mjs hasText matches converted
  to case-insensitive regex because the doctrine uppercases every
  user-visible label via text-transform: uppercase.
- qa/snap_all_scenes.mjs captures 9 fresh 1440x900 screenshots
  in qa/screenshots/v4/ (landing, mission procurement, mission AR
  blueprint, inspector raw, command palette, studio, settings,
  run history, mission live with console).

VERIFY
- tsc -b clean
- vite build green (CSS 41 KB / 8 KB gz, JS 851 KB / 230 KB gz)
- vitest 5 files / 24 tests green
- main smoke 27/27, 0 console errors
- blueprint smoke 15/15, 0 console errors
- palette audit clean (31 files)

ORACLE-REVIEWED
Round 1 PASS with <promise>VERIFIED</promise>. Two non-blocking
audit hardening notes landed in this same commit: scan JSON,
catch rgb/hsl/named CSS colors.

Confidence: high
Scope-risk: moderate (theme sweep, all scenes touched)
Not-tested: pixel-level visual diff vs prior theme (Oracle could
not inspect images this round; relied on programmatic checks)
2026-06-14 02:12:11 +04:00
shad cb9291b225 feat: real backend mutations + Studio + Settings + live API console
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)
2026-06-14 01:19:36 +04:00
shad e3b4ed62c0 fix(oracle-r2): same-origin baseUrl + refresh actually re-fetches
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)
2026-06-14 00:26:38 +04:00
shad 3ffd0e68a7 Mission Control demo v2
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
2026-06-14 00:09:32 +04:00