TEDY.07 — Spaces
State: Built — list + TxResourceBar detail with Summary · Metadata (Core + GitHub edit) · linked Code Systems / Value Sets / Map Sets (add-as-link + unlink) · Comparison (diff) · Repository (git). Create page + list Add. Simplified scope (below) confirmed with the product owner.
Template: tool-feature. Prefix: TEDY.
Feature grouping
Root spec for the Spaces family (a “Management” section resource). References TEDY.08 Servers (bound servers + the cross-server comparison).
Overview
A Space is an authoring workspace that groups terminology resources (CS / VS / MS), optionally bound to terminology servers and a git repository (GitHub, with Azure DevOps as a feature-flagged sibling) for round-tripping FHIR content. Served off /spaces (NOT /ts/*); numeric id.
Design decisions:
- TEDY renders the space detail through the shared
TxResourceBar(same tabbed shell as CS/VS/MS/Servers), NOT the reference editor’s matrix-param “context” shell (;s=;p=;v=). Comparison and Repository are tabs on that bar. - Simplified scope (deliberately narrower than the reference): no package-manager UI, no SSG generator, no ACL editor, no wiki/GitHub import. mdBook (replacing the SSG) is a future concern and is not implemented.
- Resources map directly to the Space. The UI shows the space’s linked resources as one flat set per type; “Add” links an existing resource. Because the backend only persists links through
Package → PackageVersion → PackageResource, this is backed by an auto-managed, hidden default package (one package, one version) — the package/version layer is never surfaced in the UI. - Metadata = Core + GitHub only.
Data model
Space (libs/tedy/src/lib/types/space.ts)
id:number, code, names:LocalizedName, active, shared, globalSearch, terminologyServers:string[], integration:{ github?:{repo,dirs}, msDevops?:{repo,dirs} }.
Package / PackageVersion / PackageResource (libs/tedy/src/lib/types/spacePackage.ts) — internal
The hidden layer backing “map resources directly to the space”:
Package { id?, code?, status?, spaceId? }(statusdraft/active).PackageVersion { id?, version?, description?, packageId?, resources?: PackageResource[] }.PackageResource { id?, resourceType: 'code-system'|'value-set'|'map-set', resourceId, resourceVersion?, terminologyServer?, versionId? }— a reference to an existing CS/VS/MS, not an owned copy.PackageTransactionRequest { pack: Package, version?: PackageVersion }(one transactional save; the backend retains the full resource list — resources not in the payload are soft-cancelled).SpaceDiff { items?: SpaceDiffItem[], error? },SpaceDiffItem { id?, resourceId?, resourceType?, resourceServer?, upToDate? }.- Git:
SpaceGitStatus { sha?, files?: Record<path,'M'|'U'|'D'|'A'|'K'> },SpaceGitDiff { left?, right? }.
REST API (base /spaces)
- Space:
GET/POST /spaces,GET /spaces/{id},GET /spaces?{params}. Save =saveSpace(POSTcreate /PUT-style upsert). - Packages (hidden):
GET/POST /spaces/{spaceId}/packages,GET …/{id}/versions. Save body =PackageTransactionRequest. - Add-as-link resolves the default package + latest version (
loadSpaceDefaultVersion), appends/removes aPackageResource, and re-saves the whole version (linkResourceToSpace/unlinkResourceFromSpace,libs/tedy/src/lib/client/spacePackageLink.ts). Dedup byresourceType + resourceId; creates the default package ({code: space.code||'default', status:'draft'}+ version1.0.0) if absent. - Diff:
GET /spaces/{id}/diff→ Lorque 202{id}; pollGET /lorque-processes/{id}untilfinished/failed;resultis base64 JSON.runSpaceDiff(spaceDiffClient.ts) drives start→poll→decode and rejects with the decoded error onfailed. - Git (
github|msdevops,spaceGitClient.ts):GET /spaces/{p}/providers,POST /spaces/{id}/{p}/authenticate {returnUrl}→{isAuthenticated,redirectUrl},GET /spaces/{id}/{p}/status,GET /spaces/{id}/{p}/diff?file=,POST /spaces/{id}/{p}/push {message,files},POST /spaces/{id}/{p}/pull {files}. - Resource lists are linked-resource reads: the CS/VS/MS search endpoints resolve
spaceIdby JOINing throughpackage_version_resource → package_version → package → space, sosearchCodeSystems({spaceId})returns exactly the linked set.
Metadata — attributes (Core + GitHub)
| Section | Fields | Notes |
|---|---|---|
| Core | code (required), name (AppLocalizedName), active, globalSearch, servers (multiselect of terminology-server codes) | view ⇄ edit via useDataController |
| GitHub | enable + repo (addonBefore=https://github.com/) + per-provider directory inputs (GET /spaces/github/providers) | object-defaulting before binding; save nulls disabled integrations |
| Azure DevOps | same block, feature-flagged (VITE_TX_MSDEVOPS_ENABLED) | hidden unless the flag is on |
ACL, SSG (metadata + generator), packages sub-list and import-from-GitHub are intentionally out of scope.
Screens / tabs (via TxResourceBar)
- List — Code · Name · Active · GitHub; search + Add (gated
*.Space.write→spaces/new). - Create (
SpaceCreate.tsx,spaces/new) — Core-only;saveSpace→ navigate to the new space’s Summary. - Summary — space info card.
- Metadata (
SpaceMetadata.tsx) — read-only view + inline edit (Core + GitHub / MS DevOps). - Code Systems / Value Sets / Map Sets (
SpaceResourceListTab.tsx) — list the space’s linked resources; Add = link an existing resource (searchable picker of existing CS/VS/MS, excluding the space’s full linked set →linkResourceToSpace); per-row Unlink (AppPopconfirm→unlinkResourceFromSpace). - Comparison (
SpaceDiffTab.tsx, modediff) — Run comparison → start the Lorque diff job → poll → table of per-resource/server up-to-date state; idle/loading/error/empty states. - Repository (
SpaceGitTab.tsx, modegithub) — authenticate → Connect (OAuth redirect) when not authenticated, else working-tree status (changed files); Push (commit-message modal, neutral default"update space") + Pull (confirm) + per-file diff modal. Provider-parametrized so MS DevOps reuses the same component under the feature flag.
Routes & nav
/spaces (list), /spaces/new (create). Nav: Management → Spaces. Children under spaces/:id: summary, details, code-systems, value-sets, map-sets, diff, github. TxResourceMode includes diff/github; tabsFor('space') and SpaceResourceLayout.MODES list all seven.
Privileges
*.Space.read (view/list), *.Space.write (add), {id}.Space.write (edit / link / git). Gate with usePrivileged().
Config
VITE_TX_MSDEVOPS_ENABLED (config/tx.ts → MSDEVOPS_ENABLED) toggles the Azure DevOps integration UI. Real repo/server URLs stay in the gitignored .env.
Naming
The reference’s default commit message is a brand string — TEDY uses a neutral default ("update space"). Never commit the reference product names. (Backend runtime errors surfaced in the Comparison tab may echo server-side class names; that text is server data, not TEDY source.)
Reference ↔ TEDY comparison
Re-verified against the reference editor’s space module (sys/space/containers/{space,server,package}) and the terminology-server release/feature docs. The reference drives the space detail through a matrix-param context shell (;s=;p=;v=) with a package/version drawer; TEDY replaces that with the shared TxResourceBar (point 2) — same navigation model as CS/VS/MS/Servers, no matrix params.
View / tab mapping (points 1, 4)
| Reference view | TEDY tab (TxResourceBar) | Status |
|---|---|---|
| Space dashboard (linked resources per type) | Code Systems / Value Sets / Map Sets tabs | Built — linked-resource lists + add-as-link + unlink + bulk actions |
space-edit (metadata + packages + SSG + ACL) | Metadata tab (view ⇄ edit) | Built (Core + GitHub); packages/SSG/ACL descoped |
space-diff (single-resource 3-pane) | Comparison tab | Built — whole-space diff table (Lorque) |
space-diff-matrix (resource × server grid) | Comparison tab | Built — diff items + State filter + row selection + bulk change-server + per-row sync (local/external) |
space-github | Repository tab | Built — auth / status / push / pull / per-file diff |
space-ms-dev-ops | Azure DevOps tab | Built — dedicated msdevops tab/route (flagged), reuses the provider-parametrized SpaceGitTab |
| — | Summary tab | Built (space info card; TEDY addition) |
Metadata attribute comparison (point 3)
| Reference field(s) | Section | TEDY status |
|---|---|---|
code, names, active, globalSearch, terminologyServers | Core | Built (view + edit) |
integration.github (repo + per-provider dirs) | GitHub | Built |
integration.msDevops (repo + dirs) | Azure DevOps | Built, feature-flagged (VITE_TX_MSDEVOPS_ENABLED) |
| packages sub-list (open / add / delete) | Packages | Descoped — replaced by the hidden auto-default package (resources map directly to the space) |
description, languages, defaultLanguage, siteUrl | SSG metadata | Planned (mdBook) — additive/nullable when mdBook lands |
ssgSkin, ssgThemeAccent, ssgThemeSwitcher, ssgFooterMessage, ssgFooterCopyright, ssgTxServer, ssgSearch, ssgLogo | SSG generator | Descoped — mdBook replaces the SSG generator |
acl (owners/editors/viewers), shared | ACL | Descoped — access enforced via *.Space.* privileges |
import-from-GitHub (POST /wiki-import/github) | Import | Descoped — wiki import out of scope |
Remaining gaps & implementation plan (point 5)
Everything the approved simplified scope calls for is built (points 2, 4, 6). Items 1–3 below (the reference deltas) are now also built; item 4 waits on the mdBook decision.
- Comparison matrix (
space-diff-matrix) — Built. The Comparison tab lists diff items with a State filter (up-to-date / differs), row selection, a bulk change comparable server (POST /package-resources/change-server {resourceIds, terminologyServer}, options from the space’sterminologyServers), and a per-row Sync menu — into space (local) / to server (external) — viaPOST /package-resources/{id}/sync(packageResourceClient.ts). - Per-resource Comparison shortcut — Built. Each linked CS/VS/MS row has a Compare action opening the Comparison tab.
- Dedicated MS DevOps tab — Built.
msdevopsadded toTxResourceMode+tabsFor('space')(shown only whenMSDEVOPS_ENABLED) +SpaceResourceLayout.MODES+ aspaces/:id/msdevopsroute renderingSpaceGitTab provider="msdevops"(provider-aware label/icon/empty-state). - mdBook metadata (future) — when the mdBook direction lands, add an mdBook section to Metadata for
description/languages/defaultLanguage/siteUrl(additive, nullable). Not the old SSG generator.
Backend note: the space diff needs a comparable-server-configured space; on the dev backend it returns TC105: Terminology server for current installation is not defined, so the populated matrix (and per-row sync) are verified only against a configured deployment — the error/empty paths are verified in dev.
See open questions in TEDY-new-modules-open-questions.md.