Story: Workspaces: finish the feature

Table of Contents

This page documents a story in Product backlog — inbox. It captures the goal, current status, acceptance criteria, and the tasks that compose it.

Goal

Finish the workspace feature. Two concrete user-visible outcomes:

  1. The Live workspace is properly set up and every workspace-aware entity behaves correctly under it (the default, always-on data context every user starts in).
  2. Users can create stand-alone / temporary workspaces – isolated, named data contexts that inherit from Live but don't pollute it – for use cases like importing ORE samples to explore without touching production data.

This story consolidates every workspace-related capture and story scattered across the backlog and past sprints (see * Decisions for the full inventory) into one place, so the feature can be finished as a single coherent piece of work instead of drip-feeding partial pieces.

Prior work already done (do not re-do)

  • Sprint 17 "Workspace" (../../../versions/v0/sprint_17/workspace/story.org): bitemporal schema, service layer, CRUD UI, lifecycle operations (archive/etc), codegen propagation of workspace_id (Phases 1-4), Data Workshop UI tree (Open/Add/Edit/Archive/Delete). DONE.
  • Sprint 18 "Add support for running ORE samples" (../../../versions/v0/sprint_18/ore_samples_support/story.org): workspace promoted to a full standard entity (history, generated detail dialog); ORE sample import creates a named, isolated workspace end-to-end (Track 1 of the original import-tracks design); Data Import Session window; results surfaced back in the workspace context. DONE.
  • Sprint 20 codegen org-model migration for the workspace model. DONE.
  • Sprint 23 workspace history-dialog migration to the generic HistoryDialog. ABANDONED – superseded by the Qt-commissioning task folded into this story (see Tasks).

Current architecture (already implemented, see projects/ores.workspace/core/modeling/multi_workspace.org): Live workspace is a per-tenant row using the well-known sentinel UUID aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa, seeded automatically for every tenant; workspace hierarchy/inheritance resolution via a recursive CTE; three-tier permission model; session-wide (not yet per-window) workspace activation in the Qt client.

What's explicitly NOT done yet (the actual gap)

  • Track 2 – production live trade import: Pending Book, is_live markers on reference data, a generalised Data Import Session UI (FpML/CSV/manual sources, not just ORE XML), a pending trade FSM state, and the booking/promotion workflow that moves a trade from a temporary holding area into the Live workspace. Fully designed in Data Import Session and Workflow Partial-Success (STATUS: MINED, but only Track 1 was ever converted into shipped work – Track 2 was explicitly out of scope of the Sprint 18 story) – never implemented. This is the connection point with the Trade Blotter story below: a temporary holding location for trades before they are "promoted" into the Live workspace.
  • Per-window workspace selector (side-by-side comparison of the same data under two workspaces) – deferred/future in the architecture doc.
  • "Clone to workspace" action on report definitions – unchecked in the original design's Phase 4.
  • Workspace storage/size reporting (Baobab-style visualisation) – open question, deferred.
  • Whether every workspace-impacted entity actually has full, consistent, codegen-generated workspace-aware UI/service/repo support (vs. hand-patched per entity) is unknown – this is exactly what the audit task below establishes.

Status

Field Value
State STARTED
Parent sprint Product backlog — inbox
Now Trading instrument codegen pilot landed (equity_position_instrument); the three instrument-port tasks have been re-parented to the Port trading instruments to codegen story in Sprint 25 (see Decisions).
Waiting on Nothing directly.
Next Continue workspace-specific follow-ups (audit-driven items); instrument conversions now track in their own story.
Last touched 2026-08-08

Acceptance

  • Code audit complete: every entity that should be workspace-aware is inventoried, along with whether it has codegen-generated workspace-scoping support (analogous to the existing pagination/page codegen support) or hand-patched support, and what common hand-crafted logic exists that codegen should absorb so new entities get workspace support automatically.
  • Live workspace confirmed correct end-to-end for every workspace-aware entity (no silent fallback bugs, no entity that forgot the workspace_id column or query filter).
  • Users can create a stand-alone/temporary workspace and import ORE samples into it without touching Live data (this already works per Sprint 18 – acceptance here is confirming it still holds and is not regressed, plus closing the open codegen-drift/commissioning follow-ups below).
  • The three open codegen follow-up items are resolved: workspace entity model drift fixed, workspace-cpp safe drift applied (has_party_id + hand-crafted service/repository/protocol restored correctly), and ores.qt.workspace fully commissioned into codegen.

Tasks

Task State Start End Description
Audit: current state of the workspace feature (backend + UI) DONE 2026-08-04 2026-08-04 Establish where we actually are before scoping further workspace work: inventory every entity impacted by workspace_id, whether it has a codegen-generated workspace-aware widget (analogous to page/pagination support), what is hand-crafted vs generated, and what common hand-crafted logic should be pulled into codegen so future entities get workspace support for free.
Upgrade repository template: exact-match workspace_id to resolution-order inheritance DONE 2026-08-04 2026-08-05 The cpp_domain_type_repository.cpp.mustache has_workspace_id block generates workspace_id == wid (exact match) instead of workspace_id = ANY(:resolution_order) with DISTINCT ON deduplication – so single-parent workspace inheritance, the headline feature of the whole design, does not function for any entity today. Upgrade the template and re-verify against every one of the 21 currently-covered entities.

Decisions

  • Three pre-existing tasks are left in place under their current parent stories rather than re-parented here (re-parenting would break their branch metadata and blocked_on links) – tracked as in-flight component work this story depends on:
    • Fix workspace entity model drift (Codegen developer experience improvements story) – mirrors the refdata drift-fix that succeeded; now unblocked.
    • Apply safe drift to workspace-cpp (Refactor ores.codegen C++ generation story, Sprint 25) – has_party_id flag, restore hand-crafted service/repository/protocol. Its Waiting on still points at a stale pilot-task reference; needs re-triage when picked up (the pilot's real-world equivalent – refdata's zero-diff drift – is already achieved via a different, superseding initiative).
    • Commission ores.qt.workspace into codegen (own inbox story) – inventory ores.qt.workspace entities, run full-stack codegen, migrate history dialogs to the generic HistoryDialog.
  • Audit complete (see the audit task's * Result=/=* Notes for full detail). Corrects the initial hypothesis above: there is no missing Qt-side codegen flag to build, unlike pagination – workspace scoping is already generic and shared at the network layer (WorkspaceContext=/=EntityController=/=ClientManager injecting X-Workspace-Id on every request), so every Qt widget is workspace-aware for free regardless of whether its entity is hand-crafted or generated. The real, per-entity gap is entirely in the service/repository query layer:
    • Of 46 tables carrying workspace_id, only 21 have codegen coverage (13 raw flag + 8 via the workspace-scoped-lookup profile); 25 don't, including book=/=portfolio (models exist, flag was simply never added) and ~20 hand-crafted trading instrument sub-types with no model at all. Correction (found while landing the inheritance-query fix, PR #1846): this was wrong about book=/=portfolio – both use the fk-scoped-child profile, whose own defaults set has_workspace_id: true. The original audit only checked the raw flag and the workspace-scoped-lookup profile by name, missing that other profiles supply the same default. Corrected count: 23 tables genuinely uncovered (all ~20 hand-crafted trading instrument sub-types), not 25.
    • Biggest finding: the has_workspace_id repository template generates an exact-match predicate, not the resolution-order inheritance query the architecture doc describes as the target design – so single-parent inheritance, the headline feature of the whole concept, does not function for any entity today.
    • Two follow-up tasks filed to close these: fixing the inheritance query (affects all 21 covered entities equally, likely the higher- priority one), and backfilling the 25-table coverage gap.
  • Inheritance-query fix landed (see the task's * Result=/=* Notes for full detail). Fixed the template's list-all method to use workspace_id IN (chain) when a resolution chain is set (the end-to-end plumbing for this already existed and needed no changes). Verified against the 8 profile-based refdata convention entities: zero-diff regen, clean build, all tests pass including a new permanent test proving inheritance actually works.
    • The 13 trading has_workspace_id entities could not be regenerated/verified in this pass: their models use a legacy primary-key heading format a separate, already-tracked task (Fix legacy Primary key heading blocking codegen regen on 70+ models) is responsible for fixing repo-wide. The template fix itself is generic and needs no further work – once that prerequisite lands, regenerating those 13 entities picks up inheritance automatically.
    • reporting.report_definition has its own hand-written read_latest_with_resolution (a per-workspace loop + name-based dedup, appropriate for its name-keyed override semantics) – left untouched; could be migrated onto the now-fixed generated path as a small separate cleanup, not required.
  • Trading instrument sub-type codegen decision (see the task's * Result for full detail): decided to bring the ~20 hand-crafted trading instrument sub-types under codegen rather than accept them as permanently hand-crafted. Proven via an equity_position_instrument pilot: zero-diff regen across every facet, full build green, all tests pass. Found and fixed three genuine, systemic codegen bugs along the way that affect every identity/audit-field-group-nested entity (not just this one): the generator facet never implemented field-group nesting; primary-key columns didn't inherit the identity-group flag in core.py; the service facet never generated a batch-get wrapper despite the repository supporting one. Follow-on task filed to convert the remaining ~19 entities, most of which additionally need task 836BB4CE (legacy Primary key heading) landed or worked around per-entity first.
  • Instrument-port tasks re-parented (2026-08-08): the three instrument-codegen tasks this story carried – the decision/pilot (Decide fate of ~20 hand-crafted trading instrument sub-types missing codegen models), the conversions (Convert remaining ~19 hand-crafted trading instrument sub-types to codegen models), and the NATS eventing facet work (Wire NATS eventing templates into codegen and generate DB-write-to-NATS integration tests) – moved UUID-intact via compass task move into the new Port trading instruments to codegen story in Sprint 25, where the bulk of the work now tracks. This story keeps its workspace-specific tasks (audit, inheritance query). The decision record itself stays here as history.

Out of scope

  • Track 2 / production live trade import mechanics themselves – tracked in the separate, dependent Trade Blotter story.

Emacs 29.3 (Org mode 9.6.15)