Commit Graph
81 Commits
Author SHA1 Message Date
canvas-bot 63561a9703 feat(cmd): visible 'Try saying' panel under the input when empty
build-and-publish / test (pull_request) Has been cancelled
build-and-publish / image (pull_request) Has been cancelled
First-time users opening Cmd-K saw a blank input + a long list of
items. Add a 6-row 'Try saying' panel that shows real typeable
commands with kbd-styled chips and one-line hints. Hides as soon
as the user types anything, so power-users aren't slowed down.

Controlled-input wiring: useState<query>, onValueChange, reset on
close. cmdk handles the actual filtering as before.

31/31 vitest pass.
2026-06-15 04:21:35 +04:00
shad e92c5c9236 feat(topbar): aggregated notification bell (#9)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
2026-06-15 00:20:25 +00:00
shad f739eedb47 feat(wizard): per-phase preview — graph in Analyze, form in Generate (#8)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
2026-06-15 00:18:25 +00:00
shad 50b42c7110 feat(app): global backend-health banner across every scene (#7)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
2026-06-14 21:49:37 +00:00
shad 0f508293ea fix(login): show backend-health banner before the operator types (#5)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
2026-06-14 21:31:22 +00:00
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 3deb1b8888 fix(oracle-l8-r4): scrub demo.flow-master.ai from Telemetry + Landing
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
Oracle round-9 watch-out: same liveMeta.fetchedFrom leak class
existed in two more user-visible surfaces:

- src/components/Telemetry.tsx: 'source' block rendered the host
  string. Now hard-coded 'EA2'.
- src/scenes/Landing.tsx: hero paragraph said 'runs end-to-end
  through EA2 on <host>'. Dropped the host suffix entirely.

liveMeta import removed from Telemetry (no other refs). Landing
still uses liveMeta for the workItems/distinctDefs stat counters.
2026-06-14 21:02:52 +04:00
shad d203ce1740 fix(topbar): drop demo.flow-master.ai from live tag; show 'live · EA2'
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
Oracle round-9 third blocker: App.tsx topbar tag rendered
liveMeta.fetchedFrom?.replace('https://', '') which surfaced
'demo.flow-master.ai' in the topbar chip in live mode.

Replaced with the buyer-safe brand label 'live · EA2'. liveMeta
import removed (no other references). Wizard preview pane label
change and CSP recovery from prior commit stay.
2026-06-14 20:40:02 +04:00
shad 8996771ff4 revert(app): cold-start chat seed (it regressed idle-poll budget)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
The seedHeadsIfMissing path I added fired N listMessages calls on
first refresh after auth, blowing idle-poll budget (landing went to
11 reqs at 30s). Reverting to the prior behavior:

- Topbar Chat badge shows real unread when localStorage heads are
  populated (i.e. after the user has visited Chat at least once)
- Cold-start (fresh browser / cleared storage) the badge shows 0
  until Chat is opened — same accepted caveat Oracle named on
  sha-c3515707 and PASS'd

Idle-poll budget protected; Wizard 'Manual review' label change
from prior commit stays.
2026-06-14 19:42:59 +04:00
shad a6198c3832 fix(oracle-l8-r3): wizard 'Manual review' + chat cold-start seed
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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).
2026-06-14 19:30:04 +04:00
shad c3515707ab fix(oracle-l8-r2): scrub raw endpoint from Inspector action toast
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
Oracle narrow FAIL on sha-a02cc70e: Inspector.tsx:91 still emitted
'Switch to LIVE mode + sign in to execute "..." against
/api/runtime/transactions/{id}/actions/{actionId}.'

Rewritten as 'Switch to live mode and sign in to run "..." on this
case.' Matches the LeftRail and Approvals toast voice.

Source grep for user-visible /api/ in pushToast/title/JSX text returns
zero matches.
2026-06-14 19:24:48 +04: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 be20b189a3 feat(cmdk): polished command palette - real go-to list + clean hints
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
User brief: 'normal businessman could use this'. The command palette
was already wired but leaked engineering jargon and missed half the
scenes.

- 'Scenes' renamed 'Go to'; now lists all 14 user-reachable scenes
  (was 5). Hubs, Documents, Approvals, Chat, Assistant, Explainer,
  Geo - all keyboard-reachable.
- Endpoint strings ('POST /api/runtime/transactions', 'demo.flow-
  master.ai', 'bundled JSON') removed from hints. Replaced with
  buyer-safe equivalents like 'writes to EA2' / 'for developers'.
- Real-actions group: hints simplified, less raw IDs.
- Dropped 'Dispatch sidekick agent · coming soon' (dead stub).
- 'Data mode' group renamed 'Preferences' and simplified to 3
  options (refresh live, theme toggle, dev console).
2026-06-14 18:24:49 +04:00
shad 70ed7e9aa4 fix(topbar): idle-quiet badge poll (count threads, not per-thread reads)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
Idle-poll audit caught a regression I introduced in the prior commit:
the topbar badge poll iterated chatApi.listMessages per thread on each
60s tick, firing N requests per refresh. Landing scene showed 9 reqs
in a 30s idle window (failed the <=5 budget).

Now the topbar badge just shows the THREAD COUNT (not per-thread
unread), which is a single chatApi.listThreads call. Per-thread unread
math stays in the Chat scene where it belongs (already wired:
chat-thread-row.unread + .chat-unread-summary).
2026-06-14 18:16:24 +04:00
shad 5a65408612 feat(topbar): unread/queue badges + Approvals tab
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
App.tsx polls every 60s when authed + tenant known:
- chatApi.listThreads + listMessages -> count threads with last message
  from someone else after our localStorage read mark
- api.workItems filtered by tenant -> queue count

Badge UI:
- Chat tab shows amber unread pill (99+ cap)
- Approvals tab (new in topbar) shows navy queue-count pill

Also promotes Approvals to a first-class topbar tab (was landing-chip
only). 60s poll is identical to existing live-tick cadence so this
adds no extra polling load.
2026-06-14 18:07:33 +04:00
shad d89660659f feat(wizard): polished UX with preview pane and review summary
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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.
2026-06-14 18:03:54 +04:00
shad 74ba4ab86f fix(oracle-r8): approvals read-only by default + buyer-safe llm hint
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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).
2026-06-14 17:33:04 +04:00
shad c6950e7699 fix(approvals): isMachineId catches 20+ hex anywhere in string
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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.
2026-06-14 17:20:50 +04:00
shad fb7344584d fix(approvals): friendly case title (no 32-hex business_subject leaks)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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.
2026-06-14 17:18:29 +04:00
shad c2815b4f7f fix(approvals): buyer-safe toast copy; flip runtimeHealth on action failure
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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.'
2026-06-14 17:14:09 +04:00
shad d4d74cb685 fix(approvals): real runtime-values probe; disable actions only when truly down
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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.
2026-06-14 17:10:20 +04:00
shad 32c6cc635e fix(oracle-l6-r2): provenance + approvals preview + llm off pill
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
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.
2026-06-14 17:02:03 +04:00
shad 4c46c03b3b fix(app): hydrate tenantId/actor from /me on app boot when session exists
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
The prior loop tightened Approvals.loadQueue to early-return when
tenantId is null. But the standard 'user reloads page with cached
session token' path didn't populate tenantId — it was only set during
loginAs or the live-fetch path.

Added one effect in App: when isAuthed && !tenantId, call api.ping
which proxies to /api/v1/auth/me and pulls user_id + tenant_id +
email into the store. This unblocks tenant-scoped surfaces
(Approvals) after a page reload.
2026-06-14 16:45:54 +04:00
shad 739245b3d7 fix(oracle-l6): close 2 actionable caveats
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.
2026-06-14 16:40:43 +04:00
shad c9deaeb5c2 feat(documents): browse all data definitions per published process
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.
2026-06-14 16:23:31 +04:00
shad 5072e65ec3 fix(settings): scrub raw user_id + dead emails + stale repo link; add Sign out
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)
2026-06-14 16:21:12 +04:00
shad 93de195b0a fix(mobile): stack topbar + collapse approvals at <=700px
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
2026-06-14 16:18:02 +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 224c259bfd feat(approvals): real work-item queue + active-step actions wired to EA2
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.
2026-06-14 15:57:23 +04:00
shad e52c4a7d53 fix(chat): render time badge for every thread row (no missing per-thread time)
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.
2026-06-14 15:47:28 +04:00
shad 492defa08e fix(explainer): scrub residual Pi branding + add regression QA
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.
2026-06-14 15:20:28 +04:00
shad 05be9ca8dc fix(oracle-r4-caveats): wizard rewires presentation edges, chat QA proves, Pi leak
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.
2026-06-14 15:14:59 +04:00
shad bfac76dcde fix(oracle-r4): honest agent framing + 3 new QA gates
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)
2026-06-14 15:09:12 +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 0266c77875 feat(chat): unread badges, last-message previews, relative timestamps
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.
2026-06-14 14:58:21 +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