Files
inter-hub/workplans/IHUB-WP-0009-gaaf-compliance-foundation.md
Bernd Worsch 4fb87a461e
Some checks failed
Test / test (push) Has been cancelled
updated workplan effort
2026-03-31 21:32:37 +00:00

39 KiB
Raw Blame History

id, type, title, domain, repo, status, owner, topic_slug, created, updated, completed, token_cost, state_hub_sync, state_hub_workstream_id
id type title domain repo status owner topic_slug created updated completed token_cost state_hub_sync state_hub_workstream_id
IHUB-WP-0009 workplan IHF GAAF Compliance Foundation — Type Registries, Extension Manifests, and Architectural Contracts inter_hub inter-hub done custodian inter_hub 2026-03-31 2026-03-31 2026-03-31
pricing_model session_1_context_exhausted session_1_model session_1_input_tokens_est session_1_output_tokens_est session_1_cost_usd_est session_1_notes session_2_model session_2_input_tokens_est session_2_output_tokens_est session_2_cost_usd_est session_2_notes total_input_tokens_est total_output_tokens_est total_cost_usd_est calibration_note
claude-sonnet-4-6 — assumed $3/MTok input, $15/MTok output (same tier as 3.5 Sonnet; verify against current Anthropic pricing) true claude-sonnet-4-6 ~125000 ~75000 ~$1.50 Context exhausted (~200k total). High output: GAAF spec, Operational Architecture spec, IHF v0.2 spec annotations, workplan (857 lines), 6 contract files, ARCHITECTURE-LAYERS.md, migration SQL, TypeRegistry helper, TypeRegistries controller+4 views, hub_kind schema+controller+views, CLAUDE.md/Schema.sql/Types/Routes changes. claude-sonnet-4-6 ~65000 ~35000 ~$0.72 Continuation via summary injection. T04 (registry validation wired into 4 controllers, 3 maturity badges), T05 (HubCapabilityManifests controller+4 views, FrontController nav), T06 (functional-modules.md + domain-hub-extension-guide.md), T07 (LayerBoundarySpec + Test/Main), T08 (CLAUDE.md, SCOPE.md, workplan close-out). ~190000 ~110000 ~$2.22 Commit b5d73aa shows 4855 insertions/104 deletions across 47 files. Output estimate derived from line counts (~4 chars/token). Input estimate from file reads + tool results + prior context accumulation.
done 24ad18c7-f2a9-4cfd-88f0-4cbc78064bb0

IHF GAAF Compliance Foundation

Goal

Establish inter-hub as a GAAF-2026 compliant framework foundation before any domain hub (dev-hub, ops-hub, fin-hub, sec-hub) begins implementation. Close the architectural gaps identified in the GAAF review of 2026-03-31 so that Phase 9 and beyond are built on a framework with a formal extension layer, typed vocabularies, explicit contracts, and CI-enforced architectural boundaries.

Background

Phases 18 are complete. The core traceability chain, federated governance machinery, and cross-framework adapter protocol are all production-grade. The GAAF-2026 review confirmed the Core layer is strong (3.4/5.0) but revealed four critical gaps that must be closed before domain hubs can safely extend the framework:

  1. No Extension Layer — no mechanism for domain hubs to register their widget types, event types, annotation categories, or policy vocabulary with the framework. All type discriminators are unvalidated TEXT, creating vocabulary divergence risk the moment two hubs independently name things.

  2. No /contracts/ directory or ARCHITECTURE-LAYERS.md — GAAF requires these as living governance artifacts. Their absence means architectural intent is implicit in spec documents and tribal knowledge rather than machine-readable contracts in the repository.

  3. No hub kind classification — the hubs table treats the framework host (inter-hub itself) and domain consumer hubs (dev, ops, fin, sec) as structurally identical. The Phase 10 Hub Registry cannot distinguish providers from consumers without this.

  4. No architectural fitness functions — architectural constraints exist in the spec but are not automatically verified in CI. Layer boundary violations can accumulate silently.

Reference: specs/GoodSoftwareArchitectureFramework_2026.md, specs/InteractionHubFrameworkSpecification_v0.2.md, GAAF review document (2026-03-31 analysis).

Why This Workplan Precedes Phase 9

Phase 9 exposes the IHF as a versioned external API and generates an OpenAPI 3.1 specification. That specification must enumerate type discriminators (widget_type, event_type, annotation category, policy_scope) as finite enum arrays — not arbitrary strings. If the type registries do not exist before Phase 9 begins, the OpenAPI spec will document TEXT fields and the API will be incorrect by design. The type registries must be stable before the API contract is written.

Phase 10 (Hub Registry and Marketplace) is the extension layer in IHF terms. Its hub registry IS the HubCapabilityManifest table introduced here, scaled to a public-facing UI. Building Phase 10 without first establishing the manifest schema would require a breaking re-architecture mid-phase.

GAAF Compliance Targets

After this workplan, inter-hub should score:

Layer Before Target
Core 3.4 3.8
Functional 2.2 3.2
Customization 1.5 2.5
Configuration 1.6 3.0
Extensions 0.3 3.5
Cross-layer 2.3 3.5
Weighted total 2.23 ~3.3

The target moves the framework from "Needs restructuring" (≤2.4) to "Usable but vulnerable" (2.53.4), clearing the floor for Phase 9+ work. A score of ≥3.5 (Strong) is the Phase 10 exit target.

Data Artifacts Introduced

WidgetTypeRegistry, EventTypeRegistry, AnnotationCategoryRegistry, PolicyScopeRegistry, HubCapabilityManifest

Schema additions: hubs.hub_kind, maturity columns on existing contract tables.


Tasks

T01 — Governance scaffolding: /contracts/ and ARCHITECTURE-LAYERS.md

id: IHUB-WP-0009-T01
status: done
priority: high
state_hub_task_id: "dbeaa00d-a5c1-47bf-9621-58ece75dcd42"

Pure documentation. No schema or code changes. Establishes the governance artifacts required by GAAF §4, §9, and §12.

  1. Create /contracts/README.md — contract catalog listing all contracts by layer, with one-line descriptions and file links. Template:

    # IHF Contract Catalog
    **Framework:** GAAF-2026 | **Last reviewed:** YYYY-MM-DD
    
    ## Core Contracts
    - [widget-envelope-v1](core/widget-envelope-v1.md) — Required widget envelope
      attributes and format rules
    - [append-only-events-v1](core/append-only-events-v1.md) — Immutability
      invariant for interaction_events and outcome_signals
    
    ## Functional Contracts
    - [interaction-reporting-v1](functional/interaction-reporting-v1.md) — REST
      API contract for external event and annotation submission
    - [module-maturity-labels](functional/module-maturity-labels.md) — Definition
      of Experimental / Beta / Stable / Deprecated for IHF modules
    
    ## Extensions Contracts
    - [hub-capability-manifest-v1](extensions/hub-capability-manifest-v1.md) —
      Domain hub extension registration protocol
    
  2. Create /contracts/core/widget-envelope-v1.md:

    • Required data-* attributes: data-widget-id, data-hub-id, data-view-context, data-widget-type
    • Optional: data-capability-ref, data-policy-scope, data-widget-version, data-experiment-variant
    • Format rules: data-widget-id must be a valid UUID; data-hub-id must match a registered hub slug; data-widget-type must exist in the widget_type_registry
    • Version: 1.0. Immutable after activation. New requirements → v1.1 with backwards-compatible additions only.
    • Failure mode: widgets missing required attributes are logged as malformed_envelope events; they do not crash the capture pipeline.
  3. Create /contracts/core/append-only-events-v1.md:

    • Invariant: interaction_events and outcome_signals rows are never updated or deleted after insertion.
    • Enforcement: PostgreSQL triggers interaction_events_no_update, interaction_events_no_delete, outcome_signals_no_update, outcome_signals_no_delete.
    • Correction policy: erroneous events are retracted by inserting a new event of type retracted with metadata.retracted_event_id pointing to the original. The original row is never modified.
    • Failure mode: any attempt to UPDATE or DELETE raises a PostgreSQL exception with message "is append-only".
  4. Create /contracts/functional/interaction-reporting-v1.md:

    • Summarise the InteractionReportingContract DB record as a human and machine-readable contract file. Include endpoint path, accepted event types, required payload fields, auth scheme.
    • Note: the canonical source of truth is the active interaction_reporting_contracts row; this file is the discoverable declaration.
  5. Create /contracts/functional/module-maturity-labels.md:

    • Stable: public interface will not change within a major version. Removing a Stable field is a breaking change requiring a major version bump.
    • Beta: interface is finalised for typical use but edge-case fields may change with a minor-version notice. Suitable for production with awareness.
    • Experimental: interface may change without notice between patches. Use only for internal prototyping or explicit opt-in.
    • Deprecated: will be removed in the next major version. A replacement is documented in the contract.
  6. Create /contracts/extensions/hub-capability-manifest-v1.md:

    • Describes the HubCapabilityManifest schema (introduced in T05).
    • Registration workflow: create manifest in draft → declare types → activate (auto-registers declared types into their registries).
    • Invariant: once a type name is added to a registry, it cannot be deleted (only deprecated). Names are permanent — hubs may depend on them.
    • Versioning: manifest_version tracks the protocol version, not the content version. Content changes in draft do not require a version bump; a new active manifest supersedes its predecessor.
  7. Create ARCHITECTURE-LAYERS.md at the repository root using the GAAF §12.1 template. Include:

    • Layer map: Core (Hub, Widget, WidgetVersion, InteractionEvent, Annotation, traceability chain, append-only invariants, widget envelope) | Functional (RequirementCandidate lifecycle through to FederatedGovernance) | Customization (HubRoutingRule, FederatedPolicyOverlay — hub-specific routing and policy) | Configuration (hub_kind, policy_scope, hub api_key, HubCapabilityManifest) | Extensions (HubCapabilityManifest + type registries — domain hub vocabulary registration)
    • Dependency rule diagram
    • GAAF weighted scorecard (initial values from 2026-03-31 review)
    • Next review date: 2026-09-30

Exit criteria: /contracts/ exists with six contract files; README.md catalogs all of them; ARCHITECTURE-LAYERS.md exists at root with layer map and scorecard filled in.


T02 — Hub kind: classify hubs as framework | domain | shared

id: IHUB-WP-0009-T02
status: done
priority: high
state_hub_task_id: "139cad01-24fb-4586-bd64-b5e1f72d657c"

Adds the structural distinction between inter-hub (the framework host) and domain consumer hubs. Required by Phase 10 Hub Registry and by any fitness function that checks "all domain hubs have an active capability manifest".

  1. Schema addition:

    -- hub_kind: structural role of the hub within the framework
    -- 'framework' = inter-hub itself; there is exactly one framework hub
    -- 'domain'    = a bounded domain consumer (dev-hub, ops-hub, fin-hub, sec-hub)
    -- 'shared'    = a cross-domain service hub (state-hub, governance-hub)
    ALTER TABLE hubs
        ADD COLUMN hub_kind TEXT NOT NULL DEFAULT 'domain';
    
    CREATE INDEX hubs_hub_kind_idx ON hubs (hub_kind);
    
    -- Constraint: only one framework hub at a time
    CREATE UNIQUE INDEX hubs_one_framework_idx ON hubs (hub_kind)
        WHERE hub_kind = 'framework';
    
  2. Write and run migration.

  3. If a hub row representing inter-hub itself exists (slug = 'inter-hub' or equivalent), update it: UPDATE hubs SET hub_kind = 'framework' WHERE slug = 'inter-hub'. If no such row exists, this is a documentation-only concern — add a note in ARCHITECTURE-LAYERS.md.

  4. Validation in HubsController:

    • hub_kind must be one of framework | domain | shared
    • framework kind cannot be set via the web UI (read-only in forms; only settable via migration or seeding)
    • Default for new hubs created through the UI: domain
  5. Hub list view: add a kind badge column (framework=purple, domain=blue, shared=teal). Hub show page: kind badge in the header.

  6. Hub index: add a tab filter for kind (All / Framework / Domain / Shared).

Exit criteria: migration runs; hub kind badge renders correctly; constraint prevents a second framework hub; new hubs default to domain.


T03 — Type registries: registered vocabularies for widget type, event type, annotation category, and policy scope

id: IHUB-WP-0009-T03
status: done
priority: high
state_hub_task_id: "237e56e6-ee27-4947-a16b-098722b5ed42"

This is the most consequential task in the workplan. These four registries replace the implicit, unvalidated TEXT vocabularies that all type discriminators currently rely on. They are the foundation for both the Phase 9 OpenAPI enumerations and the Phase 10 marketplace's type-safe widget patterns.

  1. Schema — four registry tables:

    -- widget_type_registry: registered widget types
    CREATE TABLE widget_type_registry (
        id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
        name TEXT NOT NULL UNIQUE,
        -- canonical identifier: lowercase-hyphenated, e.g. 'pipeline-status'
        label TEXT NOT NULL,
        description TEXT,
        owner_hub_id UUID REFERENCES hubs(id),
        -- null = framework-level type (cross-domain); non-null = domain-owned
        status TEXT NOT NULL DEFAULT 'active',
        -- 'active' | 'deprecated'
        deprecated_in_favour_of TEXT,
        -- name of the replacement type, if deprecated
        created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
    );
    
    CREATE INDEX widget_type_registry_status_idx
        ON widget_type_registry (status);
    CREATE INDEX widget_type_registry_owner_hub_idx
        ON widget_type_registry (owner_hub_id);
    
    -- event_type_registry: registered interaction event types
    CREATE TABLE event_type_registry (
        id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
        name TEXT NOT NULL UNIQUE,
        label TEXT NOT NULL,
        description TEXT,
        owner_hub_id UUID REFERENCES hubs(id),
        status TEXT NOT NULL DEFAULT 'active',
        deprecated_in_favour_of TEXT,
        created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
    );
    
    CREATE INDEX event_type_registry_status_idx
        ON event_type_registry (status);
    
    -- annotation_category_registry: registered annotation categories
    CREATE TABLE annotation_category_registry (
        id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
        name TEXT NOT NULL UNIQUE,
        label TEXT NOT NULL,
        description TEXT,
        owner_hub_id UUID REFERENCES hubs(id),
        status TEXT NOT NULL DEFAULT 'active',
        deprecated_in_favour_of TEXT,
        created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
    );
    
    CREATE INDEX annotation_category_registry_status_idx
        ON annotation_category_registry (status);
    
    -- policy_scope_registry: registered policy scope names
    CREATE TABLE policy_scope_registry (
        id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
        name TEXT NOT NULL UNIQUE,
        label TEXT NOT NULL,
        description TEXT,
        owner_hub_id UUID REFERENCES hubs(id),
        status TEXT NOT NULL DEFAULT 'active',
        deprecated_in_favour_of TEXT,
        created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
    );
    
    CREATE INDEX policy_scope_registry_status_idx
        ON policy_scope_registry (status);
    
  2. Seed the framework-level vocabulary (owner_hub_id = NULL):

    Widget types (from IHF spec §6.2 and existing usage): chart, form, table, action, panel, workflow-step, recommendation, chat, diff

    Event types (from IHF spec §6.4): viewed, focused, clicked, submitted, abandoned, retried, failed, commented, flagged_confusing, flagged_helpful, blocked_by_policy, escalated, accepted_recommendation, rejected_recommendation

    Annotation categories (from schema defaults and IHF spec §6.6): friction, missing_capability, policy_conflict, trust_deficit, accessibility, workflow_bottleneck, documentation_gap, product_opportunity, governance_concern

    Policy scopes (from existing usage): internal, org-wide, external, regulatory, security

  3. Scaffold TypeRegistriesController (single controller for all four registries, scoped by registry parameter):

    • index { registry }: table of all entries for the given registry — name, label, owner hub (or "Framework"), status badge, created at
    • show { registry, id }: full detail including deprecated_in_favour_of
    • new { registry } / create: add a new type. owner_hub_id defaults to the current user's hub (if applicable). Framework-level entries (owner_hub_id = NULL) are restricted to admin users.
    • DeprecateTypeAction { registry, id }: sets status = 'deprecated', requires deprecated_in_favour_of to be set. No delete — names are permanent.
    • No edit of name after creation (names are permanent identifiers). Label and description are editable.
  4. Link "Type Registries" from global nav (admin section).

  5. Framework-level type entries are pre-seeded by the migration and not creatable via the UI without admin privileges. Hub-owned types are created by any authenticated user.

Exit criteria: all four registry tables exist; framework vocabulary is seeded; CRUD UI works; deprecation works; name edits are blocked; registry index is accessible from nav.


T04 — Registry-backed validation: type discriminators checked against registries in controllers

id: IHUB-WP-0009-T04
status: done
priority: high
state_hub_task_id: "85882f5c-e52f-44b3-b68e-3f35e7baa9e7"

Wire the four type registries (T03) into the controllers that create records using type discriminator fields. This transforms the registries from documentation artifacts into enforced contracts.

Also adds maturity to the three existing contract tables so that functional module stability is machine-readable.

Controller validations to add:

  1. WidgetsControllerCreateWidgetAction and UpdateWidgetAction:

    • Validate widget_type exists in widget_type_registry with status = 'active'
    • Validate policy_scope exists in policy_scope_registry with status = 'active'
    • Error message format: "Widget type 'X' is not registered. Register it in the Type Registry or choose an existing type."
    • Widget new and edit forms: replace widget_type free-text input with a <select> populated from widget_type_registry (active entries first; framework-level types at top, hub-owned below a divider). Same for policy_scope.
  2. InteractionEventsController and ApiInteractionEventsControllerCreateInteractionEventAction:

    • Validate event_type exists in event_type_registry with status = 'active'
    • The API controller returns HTTP 422 with {"error": "event_type 'X' not registered"} if validation fails.
    • New/create form and API docs: enumerate active event types.
  3. AnnotationsControllerCreateAnnotationAction:

    • Validate category exists in annotation_category_registry with status = 'active'
    • Annotation new form: replace category text input with <select>.
  4. HubRoutingRulesControllerCreateHubRoutingRuleAction and UpdateHubRoutingRuleAction:

    • If match_widget_type is set, validate it exists in widget_type_registry
    • If match_category is set, validate it exists in annotation_category_registry
  5. Add a shared validation helper in Application/Helper/TypeRegistry.hs:

    -- Returns Right () if the name is registered and active; Left error otherwise.
    validateRegisteredType ::
        (?modelContext :: ModelContext) =>
        Text -> Text -> IO (Either Text ())
    validateRegisteredType registryTable name = ...
    

    Use this helper from all four controllers above.

Maturity on existing contract tables:

-- Add maturity tracking to existing contract tables (T04)
ALTER TABLE envelope_emission_contracts
    ADD COLUMN maturity TEXT NOT NULL DEFAULT 'stable';

ALTER TABLE interaction_reporting_contracts
    ADD COLUMN maturity TEXT NOT NULL DEFAULT 'stable';

ALTER TABLE widget_adapter_specs
    ADD COLUMN maturity TEXT NOT NULL DEFAULT 'beta';
-- existing adapter specs are beta until explicitly promoted

Valid values: experimental | beta | stable | deprecated (consistent with /contracts/functional/module-maturity-labels.md).

Show maturity badge on the show views for each contract table.

Exit criteria: creating a widget with an unregistered widget_type returns a validation error; creating an interaction event with an unregistered event_type returns a validation error (web and API); creating an annotation with an unregistered category returns a validation error; widget and annotation forms render as select dropdowns; maturity columns exist and render badges on contract show pages.


T05 — HubCapabilityManifest: domain hub extension registration

id: IHUB-WP-0009-T05
status: done
priority: high
state_hub_task_id: "1e5ad5db-e01f-41c5-9a81-759ab4dc88dd"

Introduces the GAAF Extensions layer: a formal mechanism for domain hubs to declare the vocabulary they contribute to the framework. This is the central artifact for the extension registration goal. Phase 10's Hub Registry will build its public-facing UI directly on top of this table.

  1. Schema:

    -- HubCapabilityManifest: a domain hub's declaration of the types it
    -- introduces and the capabilities it governs.
    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',
        -- protocol version of the manifest format, not the hub's content version
        declared_widget_types JSONB NOT NULL DEFAULT '[]',
        -- array of type names referencing widget_type_registry
        declared_event_types JSONB NOT NULL DEFAULT '[]',
        declared_annotation_categories JSONB NOT NULL DEFAULT '[]',
        declared_policy_scopes JSONB NOT NULL DEFAULT '[]',
        capability_description TEXT,
        -- human-readable summary of what this hub governs
        contact TEXT,
        -- team / person responsible for this hub's vocabulary
        status TEXT NOT NULL DEFAULT 'draft',
        -- 'draft' | 'active' | 'retired'
        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
    );
    
    CREATE INDEX hub_capability_manifests_hub_id_idx
        ON hub_capability_manifests (hub_id);
    CREATE INDEX hub_capability_manifests_status_idx
        ON hub_capability_manifests (status);
    
  2. Activation semantics (the critical business logic): On ActivateManifestAction { hubCapabilityManifestId }:

    • For each name in declared_widget_types: insert into widget_type_registry (name, owner_hub_id) if not already present. If the name exists with a different owner_hub_id, return a validation error: "Type 'X' is already owned by hub Y. Coordinate with that hub to share or rename."
    • Same for declared_event_types, declared_annotation_categories, declared_policy_scopes.
    • Set status = 'active', activated_at = now().
    • Once active, declared_* arrays are read-only. To add new types, the hub operator amends the manifest: creates a new draft record (the unique constraint is per hub_id, so the active record must be retired first) or the update goes via a DraftAmendmentAction that merges new types into the active manifest with a review step.
    • A hub with hub_kind = 'domain' should have an active manifest before it creates any hub-owned type registry entries. This is enforced as a warning (not a hard block) in the activation flow.
  3. Scaffold HubCapabilityManifestsController:

    • index: table of all manifests — hub name, kind badge, status badge, declared type counts (widget / event / category / policy), activated at
    • show { id }: full detail — hub info, all declared type arrays with links to their registry entries, activation history, contact
    • new { hubId } / create: creates a draft manifest for a hub. One manifest per hub (unique constraint); if a draft exists, redirect to edit.
    • edit / update: edit draft manifest fields. Active manifests are read-only except via DraftAmendmentAction.
    • ActivateManifestAction { id }: runs the activation workflow above.
    • RetireManifestAction { id }: sets status = 'retired'. The hub's types remain in the registry but the manifest is no longer current.
  4. Hub show page: add "Capability Manifest" section — status badge (No manifest / Draft / Active / Retired), declared type summary, link to manifest show page, "Register Capabilities" button if no active manifest.

  5. Link "Extensions" from global nav (lists all active manifests).

Exit criteria: manifest can be created in draft; activation registers all declared types into registries; name conflict during activation returns an error; activated manifest is read-only on declared types; hub show page displays manifest status; extensions nav entry renders all active manifests.


T06 — Functional module maturity documentation and domain hub extension guide

id: IHUB-WP-0009-T06
status: done
priority: medium
state_hub_task_id: "dbe749b0-0ac2-437f-9ef7-d119b5b120dd"

Documentation tasks that complete the GAAF Functional and Extensions layer requirements. No schema changes.

  1. Create docs/functional-modules.md — the authoritative maturity register for all IHF functional modules:

    Module Phase introduced Maturity Stability guarantee Deprecation policy
    RequirementCandidate lifecycle Phase 2 Stable Schema and controller API frozen Major version only
    DecisionRecord + governance ledger Phase 3 Stable Schema and controller API frozen Major version only
    DeploymentRecord + OutcomeSignal Phase 4 Stable Append-only invariant permanent Major version only
    AgentProposal + review workflow Phase 5 Beta Core fields stable; confidence model may extend Minor version notice
    Cross-framework adapter contracts Phase 6 Stable EnvelopeEmissionContract v1.0 immutable Superseding contract only
    FrictionScore + BottleneckRecord Phase 7 Beta Computation algorithm may improve Minor version notice
    HubHealthSnapshot Phase 7 Beta Snapshot schema stable; score formula may change Minor version notice
    CrossHubPropagation Phase 7 Experimental Pattern detection logic evolving No notice
    WidgetOwnership + routing Phase 8 Stable Ownership audit pattern permanent Major version only
    FederatedPolicyOverlay Phase 8 Beta Activation immutability permanent; scope model may extend Minor version notice
    StewardshipRole Phase 8 Stable Point-in-time audit pattern permanent Major version only
    ArchiveRecord + lineage inspector Phase 8 Beta Soft-delete pattern stable; lineage query may deepen Minor version notice
    Type registries (this workplan) GAAF Beta Schema stable; seed vocabulary may expand Additive only
    HubCapabilityManifest (this workplan) GAAF Beta Activation semantics stable; manifest protocol may version Minor version notice

    For each module: also document known limitations and what is NOT guaranteed.

  2. Create docs/domain-hub-extension-guide.md — the integration guide for developers building a new domain hub (dev-hub, ops-hub, etc.):

    Sections:

    • What inter-hub provides — the framework services a domain hub inherits without any setup (event capture, annotation, requirements, governance, AI assistance, observability, federation)
    • Extension registration in three steps:
      1. Create a hub row with hub_kind = 'domain'
      2. Create a HubCapabilityManifest in draft — declare widget types, event types, annotation categories, policy scopes your domain introduces
      3. Activate the manifest — your types are now registered and validated by the framework
    • Naming your types — conventions: lowercase-hyphenated, prefixed with domain shortcode where collision-prone (e.g. dev-pipeline-run, fin-budget-alert, sec-vuln-widget). Framework-level types have no prefix.
    • Using framework-level types — no registration needed; any active framework type is available to all hubs
    • Cross-hub routing — how to configure HubRoutingRule entries so cross-domain candidates route correctly to your hub
    • What changes between upgrades — how to interpret the maturity labels; Stable = safe to depend on; Beta = upgrade-aware; Experimental = internal use only
    • FAQ: Can two hubs declare the same type name? (No — activate order determines ownership; coordinate with the owning hub to share.) Can a type be renamed? (No — deprecated and replaced by a new name.) Can a hub retire its manifest? (Yes — its types remain in registries but are orphaned; reassign ownership or deprecate them.)

Exit criteria: docs/functional-modules.md exists with all modules listed; docs/domain-hub-extension-guide.md exists with all three registration steps and naming conventions covered.


T07 — Architectural fitness functions in CI

id: IHUB-WP-0009-T07
status: done
priority: medium
state_hub_task_id: "09109092-c1ca-48ba-b9b6-f4bc3d747910"

Implements the GAAF §8 requirement for automated architectural verification. Adds a Test/Architecture/ test module that runs as part of the standard test command.

  1. Create Test/Architecture/LayerBoundarySpec.hs:

    Test 1 — Core immutability contract presence: Assert that Application/Schema.sql contains the four trigger names that enforce append-only semantics:

    • interaction_events_no_update
    • interaction_events_no_delete
    • outcome_signals_no_update
    • outcome_signals_no_delete Failure message: "Core append-only invariant trigger missing from schema."

    Test 2 — Contract artifact presence: Assert that the following files exist (filesystem check):

    • /contracts/README.md
    • /contracts/core/widget-envelope-v1.md
    • /contracts/core/append-only-events-v1.md
    • ARCHITECTURE-LAYERS.md Failure message: "GAAF contract artifact missing: {path}. See ARCHITECTURE-LAYERS.md §Compliance."

    Test 3 — Type registry non-empty: Query widget_type_registry, event_type_registry, annotation_category_registry, policy_scope_registry. Assert each table has ≥1 active entry. Failure message: "Type registry {table} has no active entries. Seed the framework vocabulary before running tests."

    Test 4 — No bare TEXT type discriminators in new migrations: Read Application/Schema.sql. For any column named widget_type, event_type, category, or policy_scope added after a specific marker comment -- GAAF: type registries enforced from here, assert the column definition includes a REFERENCES or CHECK constraint. Failure message: "Column {col} in {table} uses bare TEXT for a type discriminator. Reference a registry table or add a CHECK constraint."

    Test 5 — Domain hub manifest coverage (warning, not failure): Query all hubs rows with hub_kind = 'domain'. For each, check that a hub_capability_manifests record exists with status = 'active'. Log a warning (not a test failure): "Domain hub '{slug}' has no active capability manifest. Register its types via the Extension Registry before adding hub-owned type discriminators."

  2. Register Test/Architecture/LayerBoundarySpec.hs in the test suite (Test/Main.hs).

  3. Add marker comment to Application/Schema.sql after the last Phase 8 migration block:

    -- GAAF: type registries enforced from here (IHUB-WP-0009)
    -- All new type discriminator columns (widget_type, event_type, category,
    -- policy_scope) must reference a registry table or carry a CHECK constraint.
    

Exit criteria: test includes the architecture spec; Tests 13 pass against a fresh database with seed data; Test 4 passes against the current schema; Test 5 produces no warnings for a DB with a seeded framework hub and no domain hubs yet.


T08 — GAAF gate: scorecard, consistency, documentation updates

id: IHUB-WP-0009-T08
status: done
priority: high
state_hub_task_id: "7a7dfc5b-77da-4658-b1ce-971518fb8cc8"

Closes the workplan, validates GAAF compliance targets, and prepares the repository for Phase 9.

  1. Update ARCHITECTURE-LAYERS.md scorecard with post-workplan scores. Fill in the actual weighted total and compare against targets from this workplan's Background section. Document any criteria that remain below target and why.

  2. Update SCOPE.md:

    • Current state: add "GAAF compliance foundation complete (IHUB-WP-0009); type registries, extension manifests, architectural contracts, and fitness functions in place"
    • Upstream dependencies: update hub-core reference to note that HubCapabilityManifest in inter-hub now provides the DB-side of capability registration; hub-core (when implemented) will provide the shared Haskell library that domain hubs compile against.
  3. Update CLAUDE.md:

    • Active workplan: change from IHUB-WP-0005 to IHUB-WP-0010 (Phase 9)
    • Add "GAAF compliance" context block noting that type registries must be kept seeded and all new type discriminator columns must reference a registry
    • Add reference to docs/domain-hub-extension-guide.md as the entry point for new domain hub developers
  4. State Hub consistency sync: check_repo_consistency(repo_slug="inter-hub", fix=True)

  5. Integration tests (Test/):

    • HubCapabilityManifest create in draft + activate + verify type auto-registration in registries
    • Activation conflict: two hubs declaring the same type name → second activation returns a conflict error
    • Widget create with unregistered type → validation error
    • Widget create with registered type → success
    • InteractionEvent create via API with unregistered event_type → 422
    • Annotation create with unregistered category → validation error
    • Architecture spec tests pass (T07)
  6. Smoke test checklist:

    • Create a hub with hub_kind = 'domain'; verify kind badge
    • Open Type Registries nav; verify all four registries have seeded entries
    • Create a widget with an unregistered widget_type; verify error
    • Register a new widget type via the Type Registry admin UI; retry widget creation → success
    • Create a HubCapabilityManifest for the domain hub in draft; add two custom widget types; activate → verify types appear in registry with correct owner_hub_id
    • Attempt to activate a second hub's manifest using the same type name → verify conflict error
    • Open Extensions nav; verify active manifest is listed
    • Hub show page: verify "Capability Manifest" section shows active status
    • Run test; verify architecture spec tests pass with no warnings

Exit criteria: all integration tests pass; smoke test completed without errors; ARCHITECTURE-LAYERS.md scorecard shows weighted total ≥3.1; SCOPE.md and CLAUDE.md updated; State Hub consistency sync reports no errors.


Task Dependencies

T01 (contracts scaffold)  ──→ T08 (gate reads scorecard)
T02 (hub_kind)            ──→ T05 (manifest references hub_kind)
                          ──→ T07 (fitness test 5 queries hub_kind)
T03 (type registries)     ──→ T04 (validation reads registries)
                          ──→ T05 (manifest declares types into registries)
                          ──→ T07 (fitness tests 3 and 4 check registries)
T04 (validation)          ──→ T08 (gate integration tests validate creation paths)
T05 (manifest)            ──→ T06 (extension guide documents manifest workflow)
                          ──→ T08 (gate integration tests activate manifests)
T06 (maturity docs)       ──→ T08 (gate checks docs exist)
T07 (fitness functions)   ──→ T08 (gate runs architecture spec)
All T01T07               ──→ T08 (gate)

Parallelisable: T01 (documentation only) can proceed in parallel with T02T07. T02 and T03 are independent of each other and can proceed in parallel. T04 requires T03 complete. T05 requires T02 and T03 complete. T06 requires T05 complete. T07 requires T03 complete.

Phase 9 Readiness Checklist

The following must be true before IHUB-WP-0010 (Phase 9) begins:

  • All four type registries are seeded with framework vocabulary
  • All type discriminator TEXT columns are validated against registries in controllers
  • HubCapabilityManifest table exists and the activation workflow is operational
  • /contracts/ contains at minimum core/widget-envelope-v1.md, core/append-only-events-v1.md, functional/interaction-reporting-v1.md, and extensions/hub-capability-manifest-v1.md
  • ARCHITECTURE-LAYERS.md exists and is current
  • Architecture fitness functions pass in CI
  • ARCHITECTURE-LAYERS.md weighted scorecard is ≥3.1

Notes

  • Type names are permanent. Once a name enters a registry (whether via seed or manifest activation), it cannot be deleted — only deprecated. Build the seeded vocabulary carefully. Domain hub names should use a domain-shortcode prefix where collision is likely (see extension guide).

  • Manifests are the domain hub's identity contract. A domain hub that has not activated a manifest is an unregistered participant. It can still create hubs and widgets (the framework permits it) but its types are not validated, not namespaced, and not discoverable in Phase 10's marketplace.

  • This workplan does not split the shared PostgreSQL schema. Domain isolation at the database level (separate schemas or RLS) is a Phase 9+ concern. This workplan establishes the vocabulary-level isolation needed before physical isolation decisions are made.

  • hub-core remains a future concern. The Haskell shared library for domain hub bootstrapping is planned but not blocked on this workplan. HubCapabilityManifest provides the DB-side registration contract. hub-core, when implemented, will provide the compile-time Haskell types that correspond to a hub's declared vocabulary. The two are complementary, not redundant.