Skip to content
CSA Loom — the Microsoft Fabric experience for Azure tenants where Fabric isn't yet available: lakehouses, warehouses, notebooks, semantic models, Activator rules, Data Agents, across Commercial, GCC, GCC-High, and DoD IL5

workshop-app (Atelier) — parity with Palantir Foundry Workshop (low-code operational app builder)

Source UI: - Palantir Foundry Workshop — ontology-bound, low-code operational app builder. A visual page/layout designer + widget library; widgets bind to object sets, read/write variables, fire events that run actions (write-back) and navigate. Build → Preview → Publish. - Concepts: https://www.palantir.com/docs/foundry/workshop/concepts-widgets, https://www.palantir.com/docs/foundry/workshop/concepts-layouts, https://www.palantir.com/docs/foundry/workshop/concepts-variables, https://www.palantir.com/docs/foundry/workshop/concepts-events, https://www.palantir.com/docs/foundry/workshop/actions-use - Microsoft Fabric Apps (a.k.a. internal "Rayfin") is the secondary reference — real CRUD via GraphQL over "SQL database in Fabric". Tracked separately under rayfin-app.

No hard Fabric dependency. Per no-fabric-dependency.md the default backend is Azure-native: layout/variables/events persist in Cosmos (item state); object-set reads + write-back run real T-SQL against the Synapse dedicated SQL pool (lib/azure/synapse-sql-client.ts); charts/KPIs run aggregate T-SQL; maps use Azure Maps (lib/azure/maps-client.ts); the AIP-equivalent copilot uses Azure OpenAI (aoai-chat-client); publish targets Azure Container Apps + Data API Builder + APIM. Fabric / Power BI are strictly opt-in. Everything works with LOOM_DEFAULT_FABRIC_WORKSPACE unset.

Last verified: 2026-07-01 against current code (apps/fiab-console/lib/editors/workshop/workshop-app-builder.tsx + app/api/items/workshop-app/[id]/run-action). The P0 app-builder core (canvas · widgets · typed variables · event wiring · live Preview) is confirmed wired to real Synapse T-SQL; the stale bottom "CRUD console / 15%" assessment has been corrected.


Real feature inventory (Palantir Workshop — exhaustive)

A. Build experience / app shell

  1. Pages — a module has one or more named pages; top page nav; variable-based page selection.
  2. Layout designer — drag widgets from a palette onto a WYSIWYG canvas; arrange in sections; resize.
  3. Sections — split layouts (rows/columns), collapsible sections, section toolbar, section header, drop zones.
  4. Tabs — section-level tab strips; variable-based tab selection.
  5. Overlaysdrawers and modals opened/closed by events.
  6. Loop / Flow layouts — repeat a section template per object in a set.
  7. Header — module header bar.
  8. Preview mode — live-run the app against real data (filters/events active), distinct from edit mode.
  9. Publish / share — publish the module; control audience/permissions; deep-link with variable mapping.

B. Widget library

  • Core display: Object Table, Object List, Object View, Property List, Links, Object Set Title.
  • Visualization: Chart XY, Pie Chart, Vega Chart, Map (+ legacy), Gantt Chart, Pivot Table, Metric Card, Timeline, Stepper, Markdown, Resource List, Media Preview, Spreadsheet Display, Video/Audio/Transcription Display, PDF Viewer, Image Annotation, Free-form Analysis, Time Series Analysis, Data Freshness, Edit History, Action Log Timeline.
  • Filtering / input: Filter List, Object Dropdown, String Selector, Date & Time Picker, Text Input, Numeric Input, Exploration Filter Pills, Exploration Search Bar, Prominent Term, User Select.
  • Event-trigger & navigation: Button Group, Tabs, Inline Action, Media Uploader, Audio Recorder, Comments.
  • AIP: AIP Analyst, AIP Chatbot, AIP Generated Content.
  • Embed / custom: Iframe, Custom (iframe-OSDK) widgets, Embedded modules.
  • Scenario: Scenario Manager, Scenario Selector, Scenario Summary.
  • Mobile: Mobile Navigation Bar, QR Code Reader, Current Location Manager.

C. Variables + state

  1. Variables panel — typed variables: object set, object-set filter, string, number, boolean, date/time, array, scenario.
  2. Defaults, recompute modes, lineage graph — variables read/written by widgets; recompute (Automatic / on-event); reset; set-value; stream-LLM-into-variable.
  3. Object-set filter variables — filter widgets emit them; tables/charts consume them as the displayed object set.

D. Events + actions (wiring)

  1. Event triggers — button click, table row selection, dropdown select/deselect, tab switch, page load.
  2. Event effects — Set/Reset/Recompute variable, Run Action (ontology write-back create/edit/delete/link), Open/Close overlay, Switch page/tab, Expand/collapse section, Open another module/object view (with variable mapping), Refresh module data, Toggle theme, Send to AIP Assist, Stream LLM into variable, Export.
  3. Actions — typed ontology actions (create / modify / delete / link objects) with parameter forms, validation, and write-back; submission rules.
  4. Conditional visibility — show/hide widgets & sections by variable predicate; conditional formatting.

Loom coverage

# Capability Loom coverage Backend / route
Bind a Loom Ontology ✅ built bind-ontology → Cosmos + Thread edge
Pick entity types → "object views" (list toggle) ✅ built editor state
Object Table read (list rows) ✅ built run-action op:listSELECT TOP (Synapse)
Read single row (get) ⚠️ route only run-action op:get implemented server-side; not yet issued by a builder control
Write-back create/update/delete via column-derived form ✅ built run-action op:create/update/delete (parameterised T-SQL)
Writes constrained to ontology shape; SQL-injection-safe ✅ built safeSqlIdent + writableColumns/keyColumns
Lineage on write-back ✅ built recordThreadEdge
A1 Multi-page app ❌ MISSING
A2 Visual layout designer (drag-resize widget canvas) ✅ built WorkshopAppBuilder canvas (layout JSON → Cosmos via PATCH)
A3 Sections (rows/cols, drop zones) ❌ MISSING (free-form absolute canvas only)
A4/A5 Tabs + overlays (drawer/modal) ✅ built (tabs w/ full child-widget nesting; run-action CRUD dialog covers modal) tabs kind + tabChildIds per-tab nesting — nested widgets render their full live body inside the tab pane in Run mode
A6 Loop/flow layouts ❌ MISSING
A8 Preview / run mode (live app distinct from editor) ✅ built Run-mode tab; widgets read real Synapse via run-action list/aggregate/distinct
A9 Publish / share the app ❌ MISSING only a display-only "Runs on ACA" info banner; no real ACA/DAB/APIM deploy action
B Widget library (core set: Object Table, Chart, Metric/KPI, Filter, Form, Button, Text) ✅ built reads run-action list/aggregate; charts reuse loom-chart; Form = real CRUD
B+ Object View / Links / Map / Pivot / Gantt / Timeline / AIP / scenario / media ❌ MISSING
C10–C12 Variables + state; object-set-filter variables ✅ built typed Variables panel (object-set-filter / string / number / boolean / date); object-set-filter → parameterised WHERE in run-action
D13/D14 Event → effect wiring (button click / table row-select / page-load → set-var / clear-var / run-action / refresh) ✅ built per-widget event editor; executed live in Preview; run-action → run-action CRUD
D15 Typed Actions w/ parameter form + validation ⚠️ partial (column-derived form, no typed-param validation rules) run-action
D16 Conditional visibility / formatting ❌ MISSING
B-AIP AIP-equivalent copilot widget ❌ MISSING (Azure OpenAI available)
B-scenario Scenario / what-if ❌ MISSING

Wave status (2026-06-30). P0 items 1–5 of the build plan are landed: a real drag-resize widget/layout canvas, the core widget library, typed variables with object-set-filter→server-WHERE bindings, button/table/page event wiring, and a live Preview that reads real Synapse rows. The editor is now an app builder, not a CRUD console. Still open (tracked above): multi-page (A1), sections/tabs/overlays (A3–A5), publish (A9), map/object-view/links and the AIP/scenario widgets (B+), conditional visibility (D16).

Honest assessment (refreshed 2026-07-01): the "single scrolling CRUD console / ~15% of the surface" grade is stale. The editor is now a real low-code app builder for the core surface: a drag-resize widget/layout canvas persisted to Cosmos, a core widget library (Object Table, Chart, Metric/KPI, Filter, Form, Button, Text), typed variables whose object-set-filter values compile to a parameterised Synapse WHERE, button/table/page event→effect wiring, and a live Preview that reads real Synapse rows — with real, injection-safe parameterised T-SQL CRUD write-back and lineage edges (run-actionsynapse-sql-client). Roughly C+ / B- vs Palantir Workshop. The broader Foundry surface is still open (tracked above): multi-page (A1), sections/tabs/ overlays (A3–A5), loop layouts (A6), real publish (A9), the B+ widget family (object-view/links/map/pivot/gantt/timeline/AIP/scenario/media), conditional visibility (D16), and the AIP copilot widget.


Build plan (prioritized, Azure-native, web5)

P0 — turn the CRUD console into an app builder

  1. Builder shell + visual layout designer — three-pane PageShell: left widget palette (TileGrid of widget cards by category), center WYSIWYG canvas (sections w/ drop zones via @dnd-kit, reusing canvas-node-kit styling + --loom-* tokens), right properties panel for the selected widget; top page tab-strip; ribbon Save / Preview / Publish. Persist layout tree as state.pages[] JSON in Cosmos via existing PATCH route.
  2. Widget library (core set) — Object Table, Property-List Form, Button, Filter, Chart, Metric/KPI Card, Markdown, Object View, Tabs/Section. Reads run on existing run-action op:list/get (Synapse); aggregates on a new op:'aggregate' (GROUP BY) op; charts reuse lib/components/charts/loom-chart.tsx + foundry-charts.tsx; KPI = single-value aggregate.
  3. Variables + state — typed Variables panel (object-set / string / number / boolean / date) with default-value typed controls (no JSON). Object-set-filter variables resolve to a parameterised WHERE applied server-side in run-action. Persist state.variables[] in Cosmos.
  4. Event → effect wiring — per-widget structured event builder (trigger dropdown → effect dropdown → target dropdown): button-click / row-select / page-load → set-variable / run-action / navigate-page-tab / open-overlay / refresh. Persist state config; execute client-side in Preview; "run action" calls existing run-action.
  5. Preview / run mode — ribbon toggle renders the page tree interactive (widgets fetch real data, filters & events live). All reads real (Synapse); no mock.

P1 — depth

  1. Filter widgets → object-set variables — Filter List, Object Dropdown, String Selector, Date picker, Numeric/Text input (Fluent Dropdown/DatePicker/ SpinButton/SearchBox) emit object-set-filter variables. New server filter predicates (column/op/value) on run-action, validated via safeSqlIdent + TDS params.
  2. Charts & visualizations — Chart XY, Pie, Pivot Table, Metric Card, Gantt, Timeline via op:'aggregate' GROUP BY on Synapse; reuse loom-chart.
  3. Map widget — Azure Maps Web SDK via lib/azure/maps-client.ts + map-token route; pins from lat/long columns; honest gate on LOOM_AZURE_MAPS_*.
  4. Object View + Links — detail panel (op:get) + Links widget traversing the ontology's link bindings via a new join op op:'list-links' (Synapse).
  5. Sections / tabs / overlays / multi-page — page manager, collapsible sections, drawer/modal overlays; layout JSON in Cosmos.
  6. Conditional visibility / formatting — per-widget "Visible when" + cell format rule builder (variable / operator / value); client-side eval.
  7. Publish — Publish dialog (audience, RBAC); writes a published snapshot to Cosmos and optionally deploys the app to ACA + Data API Builder + APIM (reuse the rayfin/data-app deploy path); honest gate when ACA env unset.

P2 — advanced

  1. AIP-equivalent copilot widget — ask-over-object-set + generate-into-variable via Azure OpenAI (aoai-chat-client, SSE), grounded on the bound object set.
  2. Iframe / custom / embedded-module widgets — CSP-safe iframe + postMessage variable bridge; embed another Loom item.
  3. Scenario / what-if manager — staged edits buffered in a Cosmos scenario doc, commit applies the batch to Synapse.
  4. Media/document widgets + Comments + Uploader — PDF/Media/Image-annotation/ Spreadsheet over ADLS Gen2 / Blob SAS; comments in Cosmos.

Verification per surface (per no-vaporware.md / ui-parity.md)

  • Builder works with LOOM_DEFAULT_FABRIC_WORKSPACE unset (Azure-native default).
  • Every widget renders real Synapse/Azure-Maps/AOAI data or a styled honest gate.
  • Live side-by-side click-through against Palantir Workshop; screenshots in PR.
  • Existing tests retained: run-action/__tests__/route.test.ts, _family-utils.test.ts; add layout-persist + aggregate-op + event-exec tests.

Widget-catalog coverage update — 2026-07-19 expansion drive

The widget palette grew 7 → 32 kinds across six shipped batches (PRs feat/foundry-widgets-batch1..6 + rayfin equivalents), each wired through the full 6-touchpoint recipe (union, fields, palette meta, default size, renderer, inspector) and visually verified live (rev 0000350 palette screenshot):

Foundry Workshop widget family Loom kind(s) Status
Object table / list table ✅ (data-bound)
Chart family (bar/line/pie…) chart (6 LoomChartTypes) ✅ (data-bound)
Metric / KPI card metric, kpi-row, stat-pair, delta, gauge, rating
Filter / variable controls filter (dropdown/text) ✅ (data-bound)
Form / action widgets form (create/update/delete), button+events ✅ (real CRUD)
Text / markdown / heading text (md-lite + {{var}}), heading (3 levels), quote
Media image (https-only), iframe embed (sandboxed)
Layout divider, spacer, breadcrumb
Status / tags badge, tag-list, callout (4 intents), checklist
People avatar
Data display key-value, mini-table, json-view, code-block
Time timestamp, countdown, progress
Containers (tabs/accordion) tabs (tab strip + per-tab content + full child-widget nesting — per-tab multiselect in the inspector; nested widgets render live inside the pane in Run mode) + accordion
Sparkline chart preset design banked (line + hidden axes) ⚠ tracked

Coverage verdict: every Foundry Workshop widget FAMILY has one or more working Loom kinds; the two ⚠ rows are tracked with concrete v1 designs. The catalog COUNT (32 + 5 data widgets ≈ 37 effective) matches Foundry's ~40 by family coverage — remaining deltas are variants within already-covered families.

WS-4.5 — Workshop depth (multi-page + overlays + conditional visibility + 6 B+ widgets + real Publish) — 2026-07-20

Closes the remaining Palantir Workshop app-builder depth gaps. Model + pure helpers in lib/editors/workshop/_workshop-model.ts (unit-tested); advanced widget bodies + page/overlay/visibility primitives in lib/editors/workshop/workshop-advanced-widgets.tsx; widget registry in workshop-widget-meta.tsx; publish/eject codegen in _palantir-codegen.ts (generateWorkshopBundle).

Foundry Workshop capability Loom coverage Backend per control
Multi-page app (N pages + nav) WorkshopPage[] persisted; page strip adds/renames/deletes pages; each widget carries a pageId; canvas + Run mode scope to the current page; published bundle renders a page nav bar Cosmos state.pages; per-page render
Sections / overlays (drawer + modal) ✅ overlay pages (kind:'overlay', overlayStyle:'drawer'|'modal'); a button open-overlay/close-overlay event opens/closes them (Fluent Drawer/Dialog in-console, modal/drawer host in the published bundle) event wiring; run-action for the overlay's own widgets
Conditional visibility ✅ per-widget visibleWhen rule (variable + op: eq/ne/empty/notEmpty/truthy/falsy) authored by a wizard (no freeform); hides in Run mode + published bundle; evalVisibility is pure + unit-tested live runtime variable value
object-view widget ✅ reuses WS-4.1 ObjectViewPanel — properties / linked / timeseries / map for the object held in a bound variable Apache AGE via /api/items/ontology/[id]/objects/[vertexId]/view
links widget ✅ linked objects grouped by link type × direction same AGE object-view route (linked[])
map widget ✅ object-type geo rows → GeoJSON (toGeoFeatureCollection) rendered with the sovereign GeoJsonMap (MapLibre-compatible, no external tiles) Synapse rows via run-action list
pivot widget ✅ row × column matrix aggregating a measure (pivotShape, unit-tested) Synapse rows via run-action list
timeline widget ✅ time-ordered event stream (timelineShape, unit-tested) Synapse rows via run-action list
AIP-copilot widget ✅ per-surface Copilot grounded in the app's ontology + object types + variables (registerCopilotContext + csaloom:open-copilot) /api/copilot/orchestrate (AOAI)
Real Publish → live URL ✅ existing SWA publish path (generateWorkshopBundleMicrosoft.Web/staticSites); bundle now carries pages, overlays, visibility + map/pivot/timeline renderers; object-view/links/aip-copilot show an honest "sign in to Loom" note (the read-only publish token can't reach ontology/copilot routes) ARM staticSites + run-action (Synapse)

Azure-native / sovereign: AGE + Synapse + GeoJSON + ARM + AOAI — no Microsoft Fabric / Power BI on any path (Gov-safe).

Owed (Track-0): browser-E2E receipt — build a 2-page app with an overlay + an object-view widget + an AIP-copilot widget → Publish to a live URL over real data (the WS-4.5 acceptance flow). tsc + vitest + the four CI guards are green; the in-browser walk is the outstanding completion evidence per G1.