generated from coulomb/repo-seed
feat(WP-0009): IHF GAAF Compliance Foundation — type registries, extension manifests, architectural contracts
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
Implements IHUB-WP-0009: closes four GAAF-2026 gaps before domain hub work begins. - TypeRegistry helper + controllers/views (hub_kind, hub_capability_manifest) - HubCapabilityManifest entity with validation and registry linkage - ARCHITECTURE-LAYERS.md + CI-enforced boundary contracts - Alembic migration 1743724800, fitness tests (Test/Architecture/) - GAAF spec, Operational Architecture spec, domain hub extension guide - Updates to CLAUDE.md, SCOPE.md, Schema.sql, Routes, FrontController, Types state_hub_sync: pending (tunnel was STALE at completion time; run fix-consistency) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
78
contracts/README.md
Normal file
78
contracts/README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# IHF Contract Catalog
|
||||
|
||||
**Framework:** GAAF-2026
|
||||
**Last reviewed:** 2026-03-31
|
||||
**Repository:** inter-hub
|
||||
|
||||
This directory contains the versioned, machine-readable contracts for each
|
||||
GAAF-2026 layer. Contract files are the authoritative declaration of
|
||||
interface, invariants, compatibility rules, and validation requirements for
|
||||
every public surface in the framework.
|
||||
|
||||
The canonical implementation of each contract is the PostgreSQL schema and
|
||||
Haskell controllers. These files are the *discoverable declaration* — the
|
||||
human and agent-readable companion that makes intent explicit without
|
||||
requiring the source code to be read.
|
||||
|
||||
---
|
||||
|
||||
## Core Contracts
|
||||
|
||||
| Contract | File | Version | Status |
|
||||
|---|---|---|---|
|
||||
| Widget Envelope | [core/widget-envelope-v1.md](core/widget-envelope-v1.md) | 1.0 | Active |
|
||||
| Append-Only Events | [core/append-only-events-v1.md](core/append-only-events-v1.md) | 1.0 | Active |
|
||||
|
||||
Core contracts are **immutable after activation**. New requirements produce a
|
||||
new version (v1.1, v2.0); the old version remains readable for audit.
|
||||
|
||||
---
|
||||
|
||||
## Functional Contracts
|
||||
|
||||
| Contract | File | Version | Status |
|
||||
|---|---|---|---|
|
||||
| Interaction Reporting API | [functional/interaction-reporting-v1.md](functional/interaction-reporting-v1.md) | 1.0 | Active |
|
||||
| Module Maturity Labels | [functional/module-maturity-labels.md](functional/module-maturity-labels.md) | 1.0 | Active |
|
||||
|
||||
Functional contracts are **evolvable with minor-version notice**. Breaking
|
||||
changes require a major version bump and a deprecation window.
|
||||
|
||||
---
|
||||
|
||||
## Extensions Contracts
|
||||
|
||||
| Contract | File | Version | Status |
|
||||
|---|---|---|---|
|
||||
| Hub Capability Manifest | [extensions/hub-capability-manifest-v1.md](extensions/hub-capability-manifest-v1.md) | 1.0 | Active |
|
||||
|
||||
Extensions contracts govern how domain hubs register their vocabulary and
|
||||
capabilities with the framework.
|
||||
|
||||
---
|
||||
|
||||
## Contract Lifecycle
|
||||
|
||||
```
|
||||
Draft → Active → Superseded
|
||||
↓
|
||||
(never deleted — old versions remain for audit)
|
||||
```
|
||||
|
||||
A contract becomes Active when:
|
||||
- Its corresponding schema and code are deployed
|
||||
- It is referenced in `ARCHITECTURE-LAYERS.md`
|
||||
|
||||
A contract is Superseded when a new version replaces it. The old file
|
||||
remains with a `superseded_by` note at the top.
|
||||
|
||||
---
|
||||
|
||||
## Adding a New Contract
|
||||
|
||||
1. Create the file in the appropriate layer directory
|
||||
2. Follow the header format: name, version, date, status, layer
|
||||
3. Document: interface, invariants, compatibility rules, validation rules,
|
||||
failure mode
|
||||
4. Add an entry to this README table
|
||||
5. Reference it in `ARCHITECTURE-LAYERS.md`
|
||||
104
contracts/core/append-only-events-v1.md
Normal file
104
contracts/core/append-only-events-v1.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Append-Only Events Contract
|
||||
|
||||
**Name:** append-only-events
|
||||
**Version:** 1.0
|
||||
**Date:** 2026-03-31
|
||||
**Status:** Active
|
||||
**Layer:** Core
|
||||
**Immutable:** Yes — this invariant is permanent and cannot be relaxed
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Interaction events and outcome signals are the primary observational record of
|
||||
the IHF. Their integrity as an append-only log is a foundational invariant:
|
||||
governance, traceability, and antifragility all depend on the fact that the
|
||||
historical record cannot be silently rewritten.
|
||||
|
||||
---
|
||||
|
||||
## Invariant
|
||||
|
||||
The following tables are **append-only**:
|
||||
|
||||
| Table | Trigger (no update) | Trigger (no delete) |
|
||||
|---|---|---|
|
||||
| `interaction_events` | `interaction_events_no_update` | `interaction_events_no_delete` |
|
||||
| `outcome_signals` | `outcome_signals_no_update` | `outcome_signals_no_delete` |
|
||||
|
||||
No row in either table may be modified or deleted after insertion. This
|
||||
invariant is enforced at the PostgreSQL level by `BEFORE UPDATE` and
|
||||
`BEFORE DELETE` triggers that raise exceptions. It cannot be bypassed by
|
||||
application code.
|
||||
|
||||
---
|
||||
|
||||
## Enforcement
|
||||
|
||||
```sql
|
||||
-- Implemented in Application/Schema.sql
|
||||
CREATE TRIGGER interaction_events_no_update
|
||||
BEFORE UPDATE ON interaction_events
|
||||
FOR EACH ROW EXECUTE FUNCTION prevent_interaction_event_mutation();
|
||||
|
||||
CREATE TRIGGER interaction_events_no_delete
|
||||
BEFORE DELETE ON interaction_events
|
||||
FOR EACH ROW EXECUTE FUNCTION prevent_interaction_event_mutation();
|
||||
|
||||
-- Same pattern for outcome_signals
|
||||
```
|
||||
|
||||
The trigger function raises:
|
||||
```
|
||||
"interaction_events is append-only: UPDATE and DELETE are not permitted"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Correction Policy
|
||||
|
||||
Erroneous events **must not** be corrected by modifying the original row.
|
||||
The correct approach is to insert a new event:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "retracted",
|
||||
"metadata": {
|
||||
"retracted_event_id": "<uuid of original event>",
|
||||
"reason": "incorrect actor attribution"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The original event remains in the table. Downstream analysis should treat
|
||||
`retracted` events as markers that exclude the referenced event from
|
||||
calculations.
|
||||
|
||||
---
|
||||
|
||||
## Failure Mode
|
||||
|
||||
Any attempt to UPDATE or DELETE a row in `interaction_events` or
|
||||
`outcome_signals` raises a PostgreSQL exception with SQLSTATE `P0001`.
|
||||
The calling transaction is aborted. No partial mutation is possible.
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
This contract applies **only** to `interaction_events` and `outcome_signals`.
|
||||
|
||||
Other append-oriented tables (`triage_states`, `widget_ownerships`,
|
||||
`stewardship_roles`) use the same conceptual pattern (soft expiry instead of
|
||||
update, no hard delete) but are not covered by DB-trigger enforcement. Their
|
||||
append semantics are enforced by application-layer controller conventions.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Reference
|
||||
|
||||
- Functions: `prevent_interaction_event_mutation()`,
|
||||
`prevent_outcome_signal_mutation()` in `Application/Schema.sql`
|
||||
- The architectural fitness function `Test/Architecture/LayerBoundarySpec.hs`
|
||||
(Test 1) verifies these trigger names are present in the schema
|
||||
88
contracts/core/widget-envelope-v1.md
Normal file
88
contracts/core/widget-envelope-v1.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Widget Envelope Contract
|
||||
|
||||
**Name:** widget-envelope
|
||||
**Version:** 1.0
|
||||
**Date:** 2026-03-31
|
||||
**Status:** Active
|
||||
**Layer:** Core
|
||||
**Immutable:** Yes — changes require v1.1 with backwards-compatible additions only
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Widget Envelope is the metadata boundary attached to every rendered widget.
|
||||
It enables the IHF capture pipeline to attribute any DOM event to a governed
|
||||
widget without coupling to the widget's implementation.
|
||||
|
||||
Any UI technology that emits the required `data-*` attributes on a DOM element
|
||||
is a first-class IHF participant.
|
||||
|
||||
---
|
||||
|
||||
## Required Attributes
|
||||
|
||||
Every widget root element **MUST** carry all of the following:
|
||||
|
||||
| Attribute | Format | Example |
|
||||
|---|---|---|
|
||||
| `data-widget-id` | UUID (RFC 4122) | `data-widget-id="550e8400-e29b-41d4-a716-446655440000"` |
|
||||
| `data-hub-id` | Registered hub slug (TEXT) | `data-hub-id="ops-hub"` |
|
||||
| `data-view-context` | Dot-separated path (TEXT) | `data-view-context="ops.incidents.list"` |
|
||||
| `data-widget-type` | Registered name from `widget_type_registry` | `data-widget-type="chart"` |
|
||||
|
||||
---
|
||||
|
||||
## Optional Attributes
|
||||
|
||||
| Attribute | Format | Notes |
|
||||
|---|---|---|
|
||||
| `data-capability-ref` | TEXT | Links widget to a hub capability |
|
||||
| `data-policy-scope` | Registered name from `policy_scope_registry` | Defaults to `internal` if absent |
|
||||
| `data-widget-version` | Integer as string | Current widget version number |
|
||||
| `data-experiment-variant` | TEXT | A/B experiment identifier |
|
||||
| `data-requirements-thread-ref` | UUID | Links to an open AnnotationThread |
|
||||
|
||||
---
|
||||
|
||||
## Validation Rules
|
||||
|
||||
1. `data-widget-id` must be a valid UUID. Non-UUID values cause the envelope to
|
||||
be treated as malformed.
|
||||
2. `data-hub-id` must match the `slug` of a registered hub in the `hubs` table.
|
||||
Unknown hub slugs are logged but do not block event capture.
|
||||
3. `data-widget-type` must exist in `widget_type_registry` with `status = 'active'`
|
||||
at the time of capture. Events from deprecated types are accepted but flagged.
|
||||
4. `data-view-context` has no format enforcement in v1.0 beyond non-empty.
|
||||
|
||||
---
|
||||
|
||||
## Failure Mode
|
||||
|
||||
- **Missing required attribute**: the capture pipeline logs a
|
||||
`malformed_envelope` event with the missing attribute names in `metadata`.
|
||||
The original interaction event is **not** stored. This is fail-safe, not
|
||||
fail-loud — the UI does not crash.
|
||||
- **Unknown hub slug**: event is stored with `metadata.hub_warning = "unknown_hub_slug"`.
|
||||
- **Unregistered widget_type**: event is stored with
|
||||
`metadata.type_warning = "unregistered_widget_type"`. No 422 is returned to
|
||||
the frontend (retroactive registration is possible).
|
||||
|
||||
---
|
||||
|
||||
## Backwards Compatibility Rule
|
||||
|
||||
v1.0 is frozen. New optional attributes may be added in v1.1 without breaking
|
||||
existing implementations. Removing any attribute, or making an optional
|
||||
attribute required, requires a new major version (v2.0) with a migration window.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Reference
|
||||
|
||||
- Schema enforcement: `widgets.widget_type` validated against `widget_type_registry`
|
||||
on create/update in `WidgetsController`
|
||||
- Client-side: `static/ihf-annotation-launcher.js` reads `data-widget-id` and
|
||||
`data-hub-id` from the nearest ancestor with these attributes
|
||||
- Server-side envelope rendering: `widgetEnvelope` HSX helper in
|
||||
`Application/Helper/View.hs`
|
||||
155
contracts/extensions/hub-capability-manifest-v1.md
Normal file
155
contracts/extensions/hub-capability-manifest-v1.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Hub Capability Manifest Contract
|
||||
|
||||
**Name:** hub-capability-manifest
|
||||
**Version:** 1.0
|
||||
**Date:** 2026-03-31
|
||||
**Status:** Active
|
||||
**Layer:** Extensions
|
||||
**Maturity:** Beta
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Hub Capability Manifest is the formal extension registration mechanism of
|
||||
the IHF. It is the contract by which a domain hub (dev-hub, ops-hub, fin-hub,
|
||||
sec-hub) declares the vocabulary it introduces to the framework: widget types,
|
||||
event types, annotation categories, and policy scopes.
|
||||
|
||||
Without an active manifest, a domain hub is an unregistered participant. Its
|
||||
widgets and events are accepted by the framework, but its type vocabulary is
|
||||
not namespaced, not validated, and not discoverable in Phase 10's marketplace.
|
||||
|
||||
---
|
||||
|
||||
## Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE hub_capability_manifests (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||
hub_id UUID NOT NULL UNIQUE REFERENCES hubs(id),
|
||||
manifest_version TEXT NOT NULL DEFAULT '1.0',
|
||||
declared_widget_types JSONB NOT NULL DEFAULT '[]',
|
||||
declared_event_types JSONB NOT NULL DEFAULT '[]',
|
||||
declared_annotation_categories JSONB NOT NULL DEFAULT '[]',
|
||||
declared_policy_scopes JSONB NOT NULL DEFAULT '[]',
|
||||
capability_description TEXT,
|
||||
contact TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
activated_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Registration Workflow
|
||||
|
||||
```
|
||||
1. Create manifest in draft
|
||||
↓
|
||||
2. Declare types (edit declared_* arrays)
|
||||
↓
|
||||
3. Activate (ActivateManifestAction)
|
||||
↓ ← auto-registers declared types into their registries
|
||||
4. Active manifest governs the hub's vocabulary
|
||||
```
|
||||
|
||||
### Step 1 — Create in draft
|
||||
|
||||
```
|
||||
POST /HubCapabilityManifests/new?hubId=<hub-id>
|
||||
```
|
||||
|
||||
One manifest per hub (UNIQUE constraint on `hub_id`). Creating a manifest
|
||||
for a hub that already has one (in any status) requires retiring the existing
|
||||
active manifest first.
|
||||
|
||||
### Step 2 — Declare types
|
||||
|
||||
While in `draft` status, edit the `declared_*` JSONB arrays. Each array
|
||||
contains the string names of types the hub will own:
|
||||
|
||||
```json
|
||||
{
|
||||
"declared_widget_types": ["dev-pipeline-run", "dev-build-status"],
|
||||
"declared_event_types": ["dev-pipeline-started", "dev-pipeline-failed"],
|
||||
"declared_annotation_categories": ["dev-blocker", "dev-flaky-test"],
|
||||
"declared_policy_scopes": ["dev-internal"]
|
||||
}
|
||||
```
|
||||
|
||||
**Naming convention**: domain-owned types should be prefixed with the domain
|
||||
shortcode to prevent collisions (e.g. `dev-`, `fin-`, `sec-`, `ops-`).
|
||||
Framework-level types (no prefix) are owned by inter-hub and shared by all hubs.
|
||||
|
||||
### Step 3 — Activate
|
||||
|
||||
```
|
||||
POST /HubCapabilityManifests/ActivateManifest?hubCapabilityManifestId=<id>
|
||||
```
|
||||
|
||||
On activation:
|
||||
- For each name in `declared_widget_types`: insert into `widget_type_registry`
|
||||
with `owner_hub_id = hub.id` if not already present.
|
||||
- Same for `declared_event_types`, `declared_annotation_categories`,
|
||||
`declared_policy_scopes`.
|
||||
- If any declared name already exists in a registry with a **different**
|
||||
`owner_hub_id`, activation is rejected with a conflict error.
|
||||
- If any declared name exists with `owner_hub_id = NULL` (framework-level),
|
||||
activation is rejected: framework types cannot be claimed by a domain hub.
|
||||
- `status` is set to `active`, `activated_at` is set to `now()`.
|
||||
|
||||
---
|
||||
|
||||
## Invariants
|
||||
|
||||
1. **Type names are permanent.** Once a type name is registered (either via
|
||||
seed or manifest activation), it cannot be deleted from the registry —
|
||||
only deprecated. Other hubs may already depend on it.
|
||||
|
||||
2. **Activated manifests are read-only on declared arrays.** To add new types,
|
||||
retire the manifest and create a new draft, or use `DraftAmendmentAction`
|
||||
which creates an amended draft pending re-activation.
|
||||
|
||||
3. **One active manifest per hub.** The UNIQUE constraint on `hub_id` plus the
|
||||
activation workflow enforce this.
|
||||
|
||||
4. **Framework types cannot be claimed.** Type names with `owner_hub_id = NULL`
|
||||
are owned by the framework. A domain hub manifest that attempts to declare
|
||||
an existing framework type name is rejected.
|
||||
|
||||
---
|
||||
|
||||
## Status Lifecycle
|
||||
|
||||
```
|
||||
draft → active → retired
|
||||
↓
|
||||
(superseded by a new draft → active cycle)
|
||||
```
|
||||
|
||||
A retired manifest's types remain in the registry. If the hub is decommissioned,
|
||||
the types should be deprecated (not deleted) in the registry.
|
||||
|
||||
---
|
||||
|
||||
## Failure Modes
|
||||
|
||||
| Scenario | Behaviour |
|
||||
|---|---|
|
||||
| Duplicate type name (same hub) | Idempotent — skipped, not an error |
|
||||
| Duplicate type name (different hub) | Activation rejected with conflict error |
|
||||
| Framework type name claimed | Activation rejected |
|
||||
| Edit of active manifest declared arrays | Rejected — manifest is read-only |
|
||||
| Hub with no manifest creates hub-owned type | Warning in fitness function; types accepted but unmanifested |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Reference
|
||||
|
||||
- Schema: `Application/Schema.sql` (added in IHUB-WP-0009-T05)
|
||||
- Controller: `Web/Controller/HubCapabilityManifests.hs`
|
||||
- Guide: `docs/domain-hub-extension-guide.md`
|
||||
- Phase 10 dependency: Hub Registry = active manifests + health snapshots
|
||||
123
contracts/functional/interaction-reporting-v1.md
Normal file
123
contracts/functional/interaction-reporting-v1.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Interaction Reporting API Contract
|
||||
|
||||
**Name:** interaction-reporting
|
||||
**Version:** 1.0
|
||||
**Date:** 2026-03-31
|
||||
**Status:** Active
|
||||
**Layer:** Functional
|
||||
**Maturity:** Stable
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the REST API surface for external systems (non-IHP adapters, domain
|
||||
hubs, third-party tools) to submit interaction events and annotations to the
|
||||
IHF without coupling to the IHP server-side implementation.
|
||||
|
||||
---
|
||||
|
||||
## Canonical Source of Truth
|
||||
|
||||
The authoritative runtime contract is the active row in the
|
||||
`interaction_reporting_contracts` table. This file is the discoverable
|
||||
human-readable declaration. In case of conflict, the database row governs.
|
||||
|
||||
---
|
||||
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
POST /api/v1/interaction-events
|
||||
```
|
||||
|
||||
Authentication: `Authorization: Bearer <hub-api-key>`
|
||||
|
||||
The `hub-api-key` must match the `api_key` column of a registered hub.
|
||||
|
||||
---
|
||||
|
||||
## Required Request Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `widget_id` | UUID | The governed widget's stable ID |
|
||||
| `event_type` | TEXT | Must exist in `event_type_registry` with `status = 'active'` |
|
||||
| `occurred_at` | ISO 8601 timestamp | When the event occurred on the client |
|
||||
|
||||
---
|
||||
|
||||
## Optional Request Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `actor_id` | UUID | The acting user's ID |
|
||||
| `actor_type` | TEXT | `user`, `agent`, `automation`, `external_adapter` |
|
||||
| `view_context_ref` | TEXT | View path where event occurred |
|
||||
| `metadata` | JSON object | Arbitrary additional context |
|
||||
|
||||
---
|
||||
|
||||
## Response — Success (201 Created)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<uuid>",
|
||||
"widget_id": "<uuid>",
|
||||
"event_type": "clicked",
|
||||
"occurred_at": "2026-03-31T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Response — Validation Error (422 Unprocessable Entity)
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "event_type 'pipeline_started' not registered",
|
||||
"help": "Register this event type via the Type Registry admin UI or hub capability manifest before submitting."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Response — Auth Error (401 Unauthorized)
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Authorization: Bearer <hub-api-key> required"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accepted Event Types
|
||||
|
||||
Accepted event types are all entries in `event_type_registry` with
|
||||
`status = 'active'`. The framework-level seed vocabulary:
|
||||
|
||||
`viewed`, `focused`, `clicked`, `submitted`, `abandoned`, `retried`,
|
||||
`failed`, `commented`, `flagged_confusing`, `flagged_helpful`,
|
||||
`blocked_by_policy`, `escalated`, `accepted_recommendation`,
|
||||
`rejected_recommendation`
|
||||
|
||||
Domain hubs may register additional event types via `HubCapabilityManifest`.
|
||||
|
||||
---
|
||||
|
||||
## Versioning Policy
|
||||
|
||||
- v1.0: current version. Stable.
|
||||
- Adding new optional request fields: minor version (v1.1), no breaking change.
|
||||
- Changing required fields, response shape, or auth scheme: major version (v2.0).
|
||||
- Phase 9 will introduce `/api/v2/` with OAuth 2.0 replacing per-hub Bearer tokens.
|
||||
v1.0 will remain supported during the migration window.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Reference
|
||||
|
||||
- Controller: `Web/Controller/ApiInteractionEvents.hs`
|
||||
- Route: `Web/Routes.hs` (`CanRoute ApiInteractionEventsController`)
|
||||
- DB record: `interaction_reporting_contracts` (contract_version = '1.0')
|
||||
84
contracts/functional/module-maturity-labels.md
Normal file
84
contracts/functional/module-maturity-labels.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Module Maturity Labels
|
||||
|
||||
**Name:** module-maturity-labels
|
||||
**Version:** 1.0
|
||||
**Date:** 2026-03-31
|
||||
**Status:** Active
|
||||
**Layer:** Functional
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the four maturity states used to label IHF functional modules and
|
||||
contract tables. These labels communicate to domain hub developers which
|
||||
surfaces are safe to depend on long-term.
|
||||
|
||||
---
|
||||
|
||||
## Labels
|
||||
|
||||
### Stable
|
||||
|
||||
The public interface **will not change** within the current major version.
|
||||
|
||||
- Removing a field, renaming a table, or changing a column type is a **breaking
|
||||
change** and requires a major version bump (e.g. IHF v1.0 → v2.0).
|
||||
- Adding a nullable or defaulted column is **not** breaking and may occur
|
||||
without a version bump, but is documented in the changelog.
|
||||
- Suitable for all production use, including domain hub compilation dependencies.
|
||||
|
||||
### Beta
|
||||
|
||||
The interface is **finalised for typical use** but edge-case fields may change
|
||||
with a minor-version notice (e.g. IHF v0.2 → v0.3).
|
||||
|
||||
- Core workflow fields are stable; metadata, computed, or supplementary fields
|
||||
may be refined.
|
||||
- Suitable for production use with awareness: monitor the changelog before
|
||||
upgrading.
|
||||
- A Beta module will be promoted to Stable once it has been used in at least
|
||||
two deployed domain hubs without reported breaking-change issues.
|
||||
|
||||
### Experimental
|
||||
|
||||
The interface **may change without notice** between patch releases.
|
||||
|
||||
- Use only for internal prototyping, spike implementations, or with explicit
|
||||
opt-in acknowledgement.
|
||||
- Not suitable for domain hub production code.
|
||||
- A module remains Experimental until its design is confirmed by real usage.
|
||||
|
||||
### Deprecated
|
||||
|
||||
The module or field **will be removed** in the next major version.
|
||||
|
||||
- A replacement is documented in the contract or in the schema comment.
|
||||
- The deprecated item remains functional until the major version boundary.
|
||||
- Domain hubs must migrate before upgrading to the next major version.
|
||||
|
||||
---
|
||||
|
||||
## Where Labels Are Used
|
||||
|
||||
| Surface | How the label is expressed |
|
||||
|---|---|
|
||||
| Functional modules | `docs/functional-modules.md` maturity column |
|
||||
| `envelope_emission_contracts` table | `maturity` column (added in IHUB-WP-0009) |
|
||||
| `interaction_reporting_contracts` table | `maturity` column |
|
||||
| `widget_adapter_specs` table | `maturity` column |
|
||||
| Type registry entries | `status` column (`active` / `deprecated`) |
|
||||
| Contract files in `/contracts/` | `Maturity:` header field |
|
||||
|
||||
---
|
||||
|
||||
## Promotion Path
|
||||
|
||||
```
|
||||
Experimental → Beta → Stable
|
||||
↓
|
||||
Deprecated → (removed at next major version)
|
||||
```
|
||||
|
||||
Promotions are recorded in the module's entry in `docs/functional-modules.md`
|
||||
and the corresponding `maturity` column in the DB is updated.
|
||||
Reference in New Issue
Block a user