feat(P5): IHF Phase 5 complete — agent-assisted distillation
Some checks failed
Test / test (push) Has been cancelled

Adds bounded AI support to the IHF governance loop. All AI outputs are
attributed (model_ref), reviewable (AgentReviewRecord), and reversible.
No autonomous decisions; no silent requirement promotion.

- T01: Schema — agent_proposals, agent_review_records,
  confidence_annotations (migration 1743379200)
- T02: AgentProposalsController (index/show/accept/reject, idempotent
  review guard), global nav "Agent" link
- T03: SummarizeClusterAction — Claude API cluster summary on widget show
- T04: DraftRequirementAction — AI requirement draft; acceptance creates
  RequirementCandidate (human-gated)
- T05: DetectDuplicatesAction — duplicate_flag proposal on candidate show
- T06: DetectPolicySensitivityAction — policy_flag with
  ConfidenceAnnotations per concern scope
- T07: ProposeImplementationAction — impl_proposal from decision show
- T08: AgentAuditDashboardAction — autoRefresh; KPI row, unreviewed queue,
  recent proposals, attribution log matrix
- T09: integration tests, SCOPE.md updated, phase5-summary.md, flake.nix
  adds http-conduit/aeson/string-conversions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 15:54:33 +00:00
parent 1862bb295a
commit 2605c1c977
23 changed files with 1284 additions and 21 deletions

View File

@@ -26,6 +26,7 @@ data HubsController
| TriageDashboardAction { hubId :: !(Id Hub) }
| GovernanceDashboardAction { hubId :: !(Id Hub) }
| AntifragilityDashboardAction { hubId :: !(Id Hub) }
| AgentAuditDashboardAction { hubId :: !(Id Hub) }
deriving (Eq, Show, Data)
data WidgetsController
@@ -33,8 +34,10 @@ data WidgetsController
| NewWidgetAction
| ShowWidgetAction { widgetId :: !(Id Widget) }
| CreateWidgetAction
| EditWidgetAction { widgetId :: !(Id Widget) }
| UpdateWidgetAction { widgetId :: !(Id Widget) }
| EditWidgetAction { widgetId :: !(Id Widget) }
| UpdateWidgetAction { widgetId :: !(Id Widget) }
| SummarizeClusterAction { widgetId :: !(Id Widget) }
| DraftRequirementAction { widgetId :: !(Id Widget) }
deriving (Eq, Show, Data)
data InteractionEventsController
@@ -67,8 +70,10 @@ data RequirementCandidatesController
| UpdateTriageStatusAction { requirementCandidateId :: !(Id RequirementCandidate) }
| AssignReviewerAction { requirementCandidateId :: !(Id RequirementCandidate) }
| MyQueueAction
| PromoteToRequirementAction { requirementCandidateId :: !(Id RequirementCandidate) }
| LinkToDecisionAction { requirementCandidateId :: !(Id RequirementCandidate) }
| PromoteToRequirementAction { requirementCandidateId :: !(Id RequirementCandidate) }
| LinkToDecisionAction { requirementCandidateId :: !(Id RequirementCandidate) }
| DetectDuplicatesAction { requirementCandidateId :: !(Id RequirementCandidate) }
| DetectPolicySensitivityAction { requirementCandidateId :: !(Id RequirementCandidate) }
deriving (Eq, Show, Data)
data RequirementsController
@@ -87,6 +92,7 @@ data DecisionRecordsController
| DeletePolicyReferenceAction { policyReferenceId :: !(Id PolicyReference) }
| AddImplementationRefAction { decisionRecordId :: !(Id DecisionRecord) }
| DeleteImplementationRefAction { implementationChangeReferenceId :: !(Id ImplementationChangeReference) }
| ProposeImplementationAction { decisionRecordId :: !(Id DecisionRecord) }
deriving (Eq, Show, Data)
data DeploymentRecordsController
@@ -98,6 +104,13 @@ data DeploymentRecordsController
| EvaluateChangeAction { deploymentRecordId :: !(Id DeploymentRecord) }
deriving (Eq, Show, Data)
data AgentProposalsController
= AgentProposalsAction
| ShowAgentProposalAction { agentProposalId :: !(Id AgentProposal) }
| AcceptProposalAction { agentProposalId :: !(Id AgentProposal) }
| RejectProposalAction { agentProposalId :: !(Id AgentProposal) }
deriving (Eq, Show, Data)
data SessionsController
= NewSessionAction
| CreateSessionAction