feat(gems): three-pass schema migration aligning state-hub with GEMS
Implements CUST-WP-0007. Resolves inconsistencies I-1, I-2, I-5, I-6
identified in the GEMS audit (GenericEntityModellingSystem.md).
Pass 1 (e1f2a3b4c5d6): domain_id FK on extension_points and
technical_debt (replaces raw string column); repo_id FK on contributions.
Fixes domain-filtering bugs in EP/TD dashboard pages.
Pass 2 (f2a3b4c5d6e7): repo_id nullable FK on workstreams, aligning
the GEMS primary attachment with ADR-001 (repo > topic). Dashboard
pages updated to prefer repo->domain over topic->domain.
Pass 3 (a3b4c5d6e7f8): SBOMSnapshot container entity (GEMS Complex
between Repository and SBOMEntry). Ingest is now additive — each call
creates a new snapshot; history is retained. List/report endpoints
filter to latest snapshot per repo via _latest_snapshot_ids_subquery().
New endpoints: GET /sbom/snapshots/, GET /sbom/snapshots/{id}/.
Dashboard gains a Snapshot History section.
Also adds GEMS analysis artefacts: wiki/GEMS-StateHub-TypeRegistry.md,
wiki/GEMS-StateHub-SWOT.md, workplans/CUST-WP-0006 (analysis),
workplans/CUST-WP-0007 (migration, now completed).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
134
wiki/GEMS-StateHub-TypeRegistry.md
Normal file
134
wiki/GEMS-StateHub-TypeRegistry.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# GEMS State-Hub Type Registry
|
||||
|
||||
Domain-specific instantiation of the Generic Entity Modelling System
|
||||
(`wiki/GenericEntityModellingSystem.md`) for the Custodian State Hub.
|
||||
|
||||
**Status:** Draft — subject to revision pending decision DEC-GEMS-001 through DEC-GEMS-006.
|
||||
**Created:** 2026-03-02
|
||||
|
||||
---
|
||||
|
||||
## Hierarchy Overview
|
||||
|
||||
```
|
||||
Ecosystem (implicit root — singleton)
|
||||
└── Domain (Complex)
|
||||
├── Topic (Complex) organizes focus areas
|
||||
└── Repository (Complex)
|
||||
├── Workstream (Complex) organizes tasks
|
||||
├── SBOMSnapshot (Complex) organizes SBOM entries
|
||||
├── Task (Atom) ← secondary: Workstream
|
||||
├── Decision (Atom) ← secondary: Topic | Workstream
|
||||
├── TechnicalDebt (Atom)
|
||||
├── ExtensionPoint (Atom)
|
||||
├── Contribution (Atom)
|
||||
└── ProgressEvent (Atom) ← secondary: Workstream | Task | Decision
|
||||
|
||||
SBOMSnapshot
|
||||
└── SBOMEntry (Atom)
|
||||
|
||||
DependsOn (Relation, primary=Domain)
|
||||
from: Workstream → to: Workstream
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Type Registry Table
|
||||
|
||||
| Type | Kind | Primary Attachment Type | Allowed Secondary Attachments | Payload / Key Fields |
|
||||
|---|---|---|---|---|
|
||||
| **Ecosystem** | Complex | — (root) | — | name, description |
|
||||
| **Domain** | Complex | Ecosystem | — | slug, name, status |
|
||||
| **Topic** | Complex | Domain | — | slug, title, status |
|
||||
| **Repository** | Complex | Domain | Topic (optional scope annotation) | slug, name, local_path, remote_url |
|
||||
| **Workstream** | Complex | Repository | Topic (organizer) | slug, title, status, owner, due_date |
|
||||
| **SBOMSnapshot** | Complex | Repository | — | snapshot_at, source |
|
||||
| **Task** | Atom | Workstream | — | title, status, priority, assignee, due_date |
|
||||
| **Decision** | Atom | Repository | Topic \| Workstream (context) | title, type, status, rationale, deadline |
|
||||
| **TechnicalDebt** | Atom | Repository | Topic \| Workstream (context) | td_id, debt_type, severity, status |
|
||||
| **ExtensionPoint** | Atom | Repository | Topic \| Workstream (context) | ep_id, ep_type, priority, status |
|
||||
| **Contribution** | Atom | Repository | — | type, target_org, target_repo, status |
|
||||
| **ProgressEvent** | Atom | Workstream | Task \| Decision (context) | summary, event_type, author |
|
||||
| **SBOMEntry** | Atom | SBOMSnapshot | — | package_name, version, ecosystem, license_spdx |
|
||||
| **DependsOn** | Relation | Domain | — | attachments[1]=from_ws, attachments[2]=to_ws, description |
|
||||
|
||||
---
|
||||
|
||||
## Validation Invariants
|
||||
|
||||
Following GEMS §5.2:
|
||||
|
||||
1. **Primary chain must be acyclic.** No entity may be its own ancestor via primary
|
||||
attachments.
|
||||
|
||||
2. **Primary attachment kind/type must match the registry.** A Task's primary must be
|
||||
a Workstream; a Workstream's primary must be a Repository, etc.
|
||||
|
||||
3. **Context-consistency for secondary attachments.** If Task has a secondary attachment
|
||||
to a Workstream, that Workstream's primary must be the same Repository as the Task's
|
||||
context (inherited via Workstream.primary).
|
||||
|
||||
4. **Relation endpoint types must match the relation's type definition.** DependsOn.from
|
||||
and DependsOn.to must both be Workstream entities within the same Domain.
|
||||
|
||||
5. **Relation primary must be a Complex.** DependsOn.primary = Domain (the relation-space
|
||||
that "owns" the inter-workstream dependency graph).
|
||||
|
||||
---
|
||||
|
||||
## Mapping: Current Tables → GEMS
|
||||
|
||||
| Current Table | Target GEMS Type | Status | Change Required |
|
||||
|---|---|---|---|
|
||||
| `domains` | Domain (Complex) | Correct | None |
|
||||
| `topics` | Topic (Complex) | Correct | None |
|
||||
| `managed_repos` | Repository (Complex) | Mostly correct | Remove nullable topic_id; add optional secondary |
|
||||
| `workstreams` | Workstream (Complex) | **Broken** | Change primary from topic_id to repo_id |
|
||||
| `tasks` | Task (Atom) | Correct | None |
|
||||
| `decisions` | Decision (Atom) | Ambiguous | Change primary to repo_id; topic/workstream become secondaries |
|
||||
| `technical_debt` | TechnicalDebt (Atom) | **Broken** | domain string → repo_id FK |
|
||||
| `extension_points` | ExtensionPoint (Atom) | **Broken** | domain string → repo_id FK |
|
||||
| `contributions` | Contribution (Atom) | Incomplete | Add repo_id FK |
|
||||
| `progress_events` | ProgressEvent (Atom) | Ambiguous | Clarify primary vs. secondary |
|
||||
| `sbom_entries` | SBOMEntry (Atom) | **Broken** | Add SBOMSnapshot container |
|
||||
| `workstream_dependencies` | DependsOn (Relation) | Acceptable | Consider Relation entity model |
|
||||
| *(missing)* | SBOMSnapshot (Complex) | **Missing** | New table required |
|
||||
| *(missing)* | Ecosystem (Complex) | Missing | Optional singleton |
|
||||
|
||||
---
|
||||
|
||||
## Query Patterns Enabled
|
||||
|
||||
After full GEMS alignment:
|
||||
|
||||
```
|
||||
# All workstreams in a domain
|
||||
Workstream WHERE primary.primary.slug = "railiance"
|
||||
|
||||
# All open tasks for a given repo
|
||||
Task WHERE primary.primary.slug = "activity-core" AND status != "done"
|
||||
|
||||
# Dependency graph for a domain
|
||||
DependsOn WHERE primary.slug = "custodian"
|
||||
|
||||
# SBOM history for a repo
|
||||
SBOMSnapshot WHERE primary.slug = "the-custodian" ORDER BY snapshot_at DESC
|
||||
|
||||
# All tech debt in a domain (currently broken — domain is a string)
|
||||
TechnicalDebt WHERE primary.primary.slug = "custodian"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- `Topic` and `Repository` are both children of `Domain` but are distinct organizers.
|
||||
Topic = "focus area / project agenda"; Repository = "git repo / code artefact boundary".
|
||||
A Topic may align with one or more repositories, but neither owns the other.
|
||||
|
||||
- `Workstream` moving from Topic → Repository is the most disruptive change. It resolves
|
||||
the ADR-001 contradiction (workplans must live in repos, but workstreams live under topics).
|
||||
|
||||
- `ProgressEvent` retains its multi-attach flexibility (topic, workstream, task, decision)
|
||||
but those become secondary attachments. The primary should be the Workstream (or Repository
|
||||
if no workstream context).
|
||||
Reference in New Issue
Block a user