feat(P3): IHF Phase 3 complete — Governance and Decision Linkage

Implements the full governance layer:
- Schema: requirements, decision_records, policy_references,
  implementation_change_references; requirement_candidates gets
  requirement_id back-reference
- RequirementsController (index/show; promotion-only create)
- DecisionRecordsController (CRUD + policy/impl ref management)
- GovernanceDashboardAction on HubsController (AutoRefresh)
- PromoteToRequirementAction + LinkToDecisionAction on candidates
- Outcome immutability enforced at controller level (fill excludes outcome)
- Full six-outcome vocabulary with Tailwind color roles
- Integration tests for all Phase 3 paths
- FrontController: registers Phase 2 missing controllers + all Phase 3
- SCOPE.md + docs/phase3-summary.md updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 10:38:50 +00:00
parent 840b0e5c7b
commit 7f9a8dd441
23 changed files with 2039 additions and 19 deletions

View File

@@ -148,3 +148,60 @@ CREATE TABLE reviewer_assignments (
assigned_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
UNIQUE (candidate_id)
);
-- Requirements — promoted from accepted RequirementCandidates (Phase 3)
CREATE TABLE requirements (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL,
source_candidate_id UUID NOT NULL REFERENCES requirement_candidates(id) ON DELETE RESTRICT,
status TEXT NOT NULL DEFAULT 'active',
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);
CREATE INDEX requirements_source_candidate_id_idx ON requirements (source_candidate_id);
-- Decision records — governance decisions acting on requirements/candidates (Phase 3)
CREATE TABLE decision_records (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
title TEXT NOT NULL,
rationale TEXT NOT NULL,
outcome TEXT NOT NULL,
requirement_id UUID REFERENCES requirements(id) ON DELETE SET NULL,
candidate_id UUID REFERENCES requirement_candidates(id) ON DELETE SET NULL,
decided_by UUID REFERENCES users(id),
decided_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);
CREATE INDEX decision_records_outcome_idx ON decision_records (outcome);
CREATE INDEX decision_records_requirement_id_idx ON decision_records (requirement_id);
-- Policy references — editorial links from decisions to policy scope (Phase 3)
CREATE TABLE policy_references (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
decision_id UUID NOT NULL REFERENCES decision_records(id) ON DELETE CASCADE,
policy_scope TEXT NOT NULL,
constraint_note TEXT,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);
CREATE INDEX policy_references_decision_id_idx ON policy_references (decision_id);
-- Implementation change references — editorial links to work items (Phase 3)
CREATE TABLE implementation_change_references (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
decision_id UUID NOT NULL REFERENCES decision_records(id) ON DELETE CASCADE,
work_item_ref TEXT NOT NULL,
system TEXT NOT NULL DEFAULT 'github',
linked_by UUID REFERENCES users(id),
linked_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);
CREATE INDEX impl_change_refs_decision_id_idx ON implementation_change_references (decision_id);
-- Back-reference: which candidate was promoted to a requirement (Phase 3)
ALTER TABLE requirement_candidates ADD COLUMN requirement_id UUID REFERENCES requirements(id) ON DELETE SET NULL;