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:
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`
|
||||
Reference in New Issue
Block a user