2026-03-29 10:38:50 +00:00
|
|
|
module Web.Controller.DecisionRecords where
|
|
|
|
|
|
|
|
|
|
import Web.Types
|
|
|
|
|
import Web.View.DecisionRecords.Index
|
|
|
|
|
import Web.View.DecisionRecords.Show
|
|
|
|
|
import Web.View.DecisionRecords.New
|
|
|
|
|
import Web.View.DecisionRecords.Edit
|
|
|
|
|
import Generated.Types
|
|
|
|
|
import IHP.Prelude
|
|
|
|
|
import IHP.ControllerPrelude
|
2026-04-29 10:46:50 +02:00
|
|
|
import Application.Helper.AgentBridge (callAgent, checkGovernancePolicy, bridgeErrorMessage, BridgeResponse(..))
|
feat(WP-0012): IHF Phase 11 — Advanced AI Federation
- Schema: AgentRegistration, ModelRoutingPolicy, AgentDelegation,
CollectiveProposal, CollectiveProposalContribution, AiGovernancePolicy,
AgentPerformanceRecord + ALTER TABLE agent_proposals
(migration 1744156800; CHECK constraints on trust_level, status,
consensus_status — GAAF compliant)
- Bridge: scripts/llm_bridge.py (llm-connect subprocess seam) +
Application/Helper/AgentBridge.hs (callBridge, callAgent,
checkGovernancePolicy, jsonArrayTexts)
- Routing: Application/Helper/ModelRouter.hs (resolveAgent,
resolveAllAgents) + ModelRoutingPolicies CRUD
- Registry: AgentRegistrations CRUD (Index/Show/New/Edit/Performance),
DeactivateAgentAction, ComputeAgentPerformanceAction
- Delegation: AgentDelegations controller + views, DelegateSubtaskAction
with token budget enforcement at bridge call time
- Collective: CollectiveProposals controller + views,
CreateCollectiveProposalAction (fan-out → synthesis → consensus detection)
- Governance: AiGovernancePolicies CRUD + ToggleAiGovernancePolicyAction;
checkGovernancePolicy enforced at all 4 Phase 5 invocation points
- Phase 5 wiring: replaced callClaudeApi in Widgets, DecisionRecords,
RequirementCandidates with resolveAgent + callAgent + token tracking
- llm-connect feature requests: ~/llm-connect/FEATURE_REQUESTS.md
(FR-1 HTTP serve, FR-2 RoutingPolicy, FR-3 async, FR-4 BudgetTracker)
- GAAF scorecard: 3.61 (up from 3.56); Functional 3.4→3.6, Extensions 3.8→3.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:57:17 +00:00
|
|
|
import Application.Helper.ModelRouter (resolveAgent)
|
feat(WP-0013): IHF Phase 12 — Platform Memory and Continuous Learning
Closes the long-range feedback loop: outcome signals now enrich the full
traceability chain and feed back into routing, triage, and AI proposals.
Schema (T01):
- outcome_correlations (CHECK correlation_type)
- pattern_performance_records
- adaptive_threshold_configs
- institutional_knowledge_entries (GIN tsvector FTS)
- learning_insights (CHECK insight_type)
- ALTER TABLE decision_records + requirement_candidates: outcome_summary JSONB
- AFTER INSERT trigger trg_enrich_lineage on outcome_signals
- contracts/core/ updated (outcome-summary-columns-v1, append-only addendum)
Correlation engine (T02):
- Application/Helper/CorrelationEngine.hs: pure annotation→outcome SQL
- Web/Controller/OutcomeCorrelations.hs: ComputeCorrelationsAction + index
Pattern performance (T03):
- Web/Controller/PatternPerformance.hs: ComputePatternPerformanceAction
Adaptive thresholds (T04):
- Web/Controller/AdaptiveThresholds.hs: CalibrateThresholdsAction
- Application/Helper/FrictionScore.hs: applyAdaptiveWeights
Institutional knowledge (T05):
- DistilDecisionAction in DecisionRecords controller
- Web/Controller/InstitutionalKnowledge.hs: QueryKnowledgeBaseAction
Lineage enrichment (T06):
- Web/Controller/LineageEnrichment.hs: EnrichLineageAction (batch backfill)
- enrich_lineage_on_outcome_batch() PL/pgSQL helper in migration
Learning dashboard (T07):
- Web/Controller/LearningDashboard.hs: 5-panel autoRefresh view
- "Learning" nav link in FrontController
API v2 learning endpoints (T08):
- GET /api/v2/outcome-correlations, /pattern-performance, /knowledge-base/{id}
- OpenAPI schemas: OutcomeCorrelation, PatternPerformanceRecord, InstitutionalKnowledgeEntry
GAAF scorecard + docs (T09):
- Core 3.8→3.9, Functional 3.6→3.8, overall 3.61→3.68
- CLAUDE.md: IHF v0.2 complete, no active workplan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:14:15 +00:00
|
|
|
import IHP.ModelSupport (sqlQuery)
|
|
|
|
|
import qualified Data.Aeson as A
|
2026-04-12 12:17:45 +00:00
|
|
|
import Data.Coerce (coerce)
|
2026-03-29 10:38:50 +00:00
|
|
|
|
|
|
|
|
validOutcomes :: [Text]
|
|
|
|
|
validOutcomes = ["accepted", "rejected", "deferred", "split", "merged", "reframed"]
|
|
|
|
|
|
|
|
|
|
validPolicyScopes :: [Text]
|
|
|
|
|
validPolicyScopes = ["internal", "external", "regulatory", "contractual", "architectural"]
|
|
|
|
|
|
|
|
|
|
validSystems :: [Text]
|
|
|
|
|
validSystems = ["github", "linear", "jira", "other"]
|
|
|
|
|
|
|
|
|
|
instance Controller DecisionRecordsController where
|
|
|
|
|
beforeAction = ensureIsUser
|
|
|
|
|
|
|
|
|
|
action DecisionRecordsAction = do
|
fix(WP-0014/A2): close remaining pure-param and structural compilation errors
Convert all remaining `<- paramOrNothing / param / paramOrDefault /
currentUserOrNothing` monadic binds to `let` — these functions are pure
(ImplicitParams-based) in IHP v1.5, so `<-` is a type error in an IO
do-block.
Controllers fixed:
AgentDelegations, AiGovernancePolicies, Annotations, ApiConsumers,
CollectiveProposals, DecisionRecords, DeploymentRecords,
HubCapabilityManifests, HubRoutingRules, InstitutionalKnowledge,
OutcomeCorrelations, RequirementCandidates, TypeRegistries,
WebhookSubscriptions, Widgets,
Api/V2/{Annotations,InteractionEvents,Token}
WebhookSubscriptions: remove orphaned `Right () ->` case arm that was
left inside a bare `unless` block (structural parse error).
Also carries forward all in-progress fixes from the working tree:
helpers (AgentBridge, ApiRateLimit, BottleneckDetector,
CrossHubPropagation, FrictionScore),
views (CanSelect instances, HSX lambda extraction, formFor wrappers),
env/build (envrc GHCi perms, flake.nix Tailwind + GHC resource limits,
static/app.css additional Tailwind output).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:14:08 +00:00
|
|
|
let mOutcomeFilter = paramOrNothing @Text "outcome"
|
2026-03-29 10:38:50 +00:00
|
|
|
records <- case mOutcomeFilter of
|
|
|
|
|
Nothing -> query @DecisionRecord |> orderByDesc #decidedAt |> fetch
|
|
|
|
|
Just o -> query @DecisionRecord
|
|
|
|
|
|> filterWhere (#outcome, o)
|
|
|
|
|
|> orderByDesc #decidedAt
|
|
|
|
|
|> fetch
|
|
|
|
|
requirements <- query @Requirement |> fetch
|
|
|
|
|
users <- query @User |> fetch
|
|
|
|
|
render IndexView { records, requirements, users, mOutcomeFilter }
|
|
|
|
|
|
|
|
|
|
action ShowDecisionRecordAction { decisionRecordId } = do
|
2026-03-29 12:27:30 +00:00
|
|
|
record <- fetch decisionRecordId
|
|
|
|
|
policyRefs <- query @PolicyReference
|
2026-03-29 10:38:50 +00:00
|
|
|
|> filterWhere (#decisionId, decisionRecordId)
|
|
|
|
|
|> orderByAsc #createdAt
|
|
|
|
|
|> fetch
|
2026-03-29 12:27:30 +00:00
|
|
|
implRefs <- query @ImplementationChangeReference
|
2026-03-29 10:38:50 +00:00
|
|
|
|> filterWhere (#decisionId, decisionRecordId)
|
|
|
|
|
|> orderByAsc #linkedAt
|
|
|
|
|
|> fetch
|
2026-03-29 12:27:30 +00:00
|
|
|
deploymentRecords <- query @DeploymentRecord
|
|
|
|
|
|> filterWhere (#decisionId, decisionRecordId)
|
|
|
|
|
|> orderByDesc #deployedAt
|
|
|
|
|
|> fetch
|
|
|
|
|
let deploymentIds = map (.id) deploymentRecords
|
|
|
|
|
evaluations <- query @ChangeEvaluation
|
|
|
|
|
|> filterWhereIn (#deploymentId, deploymentIds)
|
|
|
|
|
|> fetch
|
|
|
|
|
mRequirement <- case record.requirementId of
|
2026-03-29 10:38:50 +00:00
|
|
|
Nothing -> pure Nothing
|
|
|
|
|
Just rid -> fetchOneOrNothing rid
|
2026-03-29 12:27:30 +00:00
|
|
|
mCandidate <- case record.candidateId of
|
2026-03-29 10:38:50 +00:00
|
|
|
Nothing -> pure Nothing
|
|
|
|
|
Just cid -> fetchOneOrNothing cid
|
2026-03-29 12:27:30 +00:00
|
|
|
users <- query @User |> fetch
|
2026-03-29 10:38:50 +00:00
|
|
|
render ShowView
|
|
|
|
|
{ record
|
|
|
|
|
, policyRefs
|
|
|
|
|
, implRefs
|
2026-03-29 12:27:30 +00:00
|
|
|
, deploymentRecords
|
|
|
|
|
, evaluations
|
2026-03-29 10:38:50 +00:00
|
|
|
, mRequirement
|
|
|
|
|
, mCandidate
|
|
|
|
|
, users
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
action NewDecisionRecordAction = do
|
|
|
|
|
requirements <- query @Requirement |> fetch
|
|
|
|
|
candidates <- query @RequirementCandidate |> fetch
|
|
|
|
|
users <- query @User |> fetch
|
|
|
|
|
let record = newRecord @DecisionRecord
|
|
|
|
|
render NewView { record, requirements, candidates, users }
|
|
|
|
|
|
|
|
|
|
action CreateDecisionRecordAction = do
|
|
|
|
|
requirements <- query @Requirement |> fetch
|
|
|
|
|
candidates <- query @RequirementCandidate |> fetch
|
|
|
|
|
users <- query @User |> fetch
|
fix(WP-0014/A2): close remaining pure-param and structural compilation errors
Convert all remaining `<- paramOrNothing / param / paramOrDefault /
currentUserOrNothing` monadic binds to `let` — these functions are pure
(ImplicitParams-based) in IHP v1.5, so `<-` is a type error in an IO
do-block.
Controllers fixed:
AgentDelegations, AiGovernancePolicies, Annotations, ApiConsumers,
CollectiveProposals, DecisionRecords, DeploymentRecords,
HubCapabilityManifests, HubRoutingRules, InstitutionalKnowledge,
OutcomeCorrelations, RequirementCandidates, TypeRegistries,
WebhookSubscriptions, Widgets,
Api/V2/{Annotations,InteractionEvents,Token}
WebhookSubscriptions: remove orphaned `Right () ->` case arm that was
left inside a bare `unless` block (structural parse error).
Also carries forward all in-progress fixes from the working tree:
helpers (AgentBridge, ApiRateLimit, BottleneckDetector,
CrossHubPropagation, FrictionScore),
views (CanSelect instances, HSX lambda extraction, formFor wrappers),
env/build (envrc GHCi perms, flake.nix Tailwind + GHC resource limits,
static/app.css additional Tailwind output).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:14:08 +00:00
|
|
|
let mUser = currentUserOrNothing
|
|
|
|
|
decidedBy = fmap (.id) mUser
|
2026-03-29 10:38:50 +00:00
|
|
|
|
|
|
|
|
let record = newRecord @DecisionRecord
|
|
|
|
|
record
|
|
|
|
|
|> fill @'["title", "rationale", "outcome", "requirementId", "candidateId", "notes"]
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #decidedBy (fmap coerce decidedBy)
|
2026-03-29 10:38:50 +00:00
|
|
|
|> validateField #title nonEmpty
|
|
|
|
|
|> validateField #rationale nonEmpty
|
2026-04-29 10:46:50 +02:00
|
|
|
|> validateField #outcome (isInList validOutcomes)
|
2026-03-29 10:38:50 +00:00
|
|
|
|> ifValid \case
|
|
|
|
|
Left record -> render NewView { record, requirements, candidates, users }
|
|
|
|
|
Right record -> do
|
|
|
|
|
created <- createRecord record
|
|
|
|
|
setSuccessMessage "Decision record created"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId = created.id }
|
|
|
|
|
|
|
|
|
|
action EditDecisionRecordAction { decisionRecordId } = do
|
|
|
|
|
record <- fetch decisionRecordId
|
|
|
|
|
requirements <- query @Requirement |> fetch
|
|
|
|
|
candidates <- query @RequirementCandidate |> fetch
|
|
|
|
|
users <- query @User |> fetch
|
|
|
|
|
render EditView { record, requirements, candidates, users }
|
|
|
|
|
|
|
|
|
|
action UpdateDecisionRecordAction { decisionRecordId } = do
|
|
|
|
|
record <- fetch decisionRecordId
|
|
|
|
|
requirements <- query @Requirement |> fetch
|
|
|
|
|
candidates <- query @RequirementCandidate |> fetch
|
|
|
|
|
users <- query @User |> fetch
|
|
|
|
|
|
|
|
|
|
-- Outcome is immutable: only update non-outcome fields
|
|
|
|
|
record
|
|
|
|
|
|> fill @'["title", "rationale", "requirementId", "candidateId", "notes"]
|
|
|
|
|
|> validateField #title nonEmpty
|
|
|
|
|
|> validateField #rationale nonEmpty
|
|
|
|
|
|> ifValid \case
|
|
|
|
|
Left record -> render EditView { record, requirements, candidates, users }
|
|
|
|
|
Right record -> do
|
|
|
|
|
updateRecord record
|
|
|
|
|
setSuccessMessage "Decision record updated"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
|
|
|
|
|
|
|
|
|
action AddPolicyReferenceAction { decisionRecordId } = do
|
fix(WP-0014/A2): close remaining pure-param and structural compilation errors
Convert all remaining `<- paramOrNothing / param / paramOrDefault /
currentUserOrNothing` monadic binds to `let` — these functions are pure
(ImplicitParams-based) in IHP v1.5, so `<-` is a type error in an IO
do-block.
Controllers fixed:
AgentDelegations, AiGovernancePolicies, Annotations, ApiConsumers,
CollectiveProposals, DecisionRecords, DeploymentRecords,
HubCapabilityManifests, HubRoutingRules, InstitutionalKnowledge,
OutcomeCorrelations, RequirementCandidates, TypeRegistries,
WebhookSubscriptions, Widgets,
Api/V2/{Annotations,InteractionEvents,Token}
WebhookSubscriptions: remove orphaned `Right () ->` case arm that was
left inside a bare `unless` block (structural parse error).
Also carries forward all in-progress fixes from the working tree:
helpers (AgentBridge, ApiRateLimit, BottleneckDetector,
CrossHubPropagation, FrictionScore),
views (CanSelect instances, HSX lambda extraction, formFor wrappers),
env/build (envrc GHCi perms, flake.nix Tailwind + GHC resource limits,
static/app.css additional Tailwind output).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:14:08 +00:00
|
|
|
let mUser = currentUserOrNothing
|
|
|
|
|
createdBy = fmap (.id) mUser
|
|
|
|
|
policyScope = param @Text "policyScope"
|
|
|
|
|
constraintNote = paramOrNothing @Text "constraintNote"
|
2026-03-29 10:38:50 +00:00
|
|
|
unless (policyScope `elem` validPolicyScopes) do
|
|
|
|
|
setErrorMessage ("Invalid policy scope: " <> policyScope)
|
2026-04-12 12:17:45 +00:00
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
2026-03-29 10:38:50 +00:00
|
|
|
newRecord @PolicyReference
|
|
|
|
|
|> set #decisionId decisionRecordId
|
|
|
|
|
|> set #policyScope policyScope
|
|
|
|
|
|> set #constraintNote constraintNote
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #createdBy (fmap coerce createdBy)
|
2026-03-29 10:38:50 +00:00
|
|
|
|> createRecord
|
|
|
|
|
setSuccessMessage "Policy reference added"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
|
|
|
|
|
|
|
|
|
action DeletePolicyReferenceAction { policyReferenceId } = do
|
|
|
|
|
ref <- fetch policyReferenceId
|
|
|
|
|
let decisionRecordId = ref.decisionId
|
|
|
|
|
deleteRecord ref
|
|
|
|
|
setSuccessMessage "Policy reference removed"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
|
|
|
|
|
|
|
|
|
action AddImplementationRefAction { decisionRecordId } = do
|
fix(WP-0014/A2): close remaining pure-param and structural compilation errors
Convert all remaining `<- paramOrNothing / param / paramOrDefault /
currentUserOrNothing` monadic binds to `let` — these functions are pure
(ImplicitParams-based) in IHP v1.5, so `<-` is a type error in an IO
do-block.
Controllers fixed:
AgentDelegations, AiGovernancePolicies, Annotations, ApiConsumers,
CollectiveProposals, DecisionRecords, DeploymentRecords,
HubCapabilityManifests, HubRoutingRules, InstitutionalKnowledge,
OutcomeCorrelations, RequirementCandidates, TypeRegistries,
WebhookSubscriptions, Widgets,
Api/V2/{Annotations,InteractionEvents,Token}
WebhookSubscriptions: remove orphaned `Right () ->` case arm that was
left inside a bare `unless` block (structural parse error).
Also carries forward all in-progress fixes from the working tree:
helpers (AgentBridge, ApiRateLimit, BottleneckDetector,
CrossHubPropagation, FrictionScore),
views (CanSelect instances, HSX lambda extraction, formFor wrappers),
env/build (envrc GHCi perms, flake.nix Tailwind + GHC resource limits,
static/app.css additional Tailwind output).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:14:08 +00:00
|
|
|
let mUser = currentUserOrNothing
|
|
|
|
|
linkedBy = fmap (.id) mUser
|
|
|
|
|
workItemRef = param @Text "workItemRef"
|
|
|
|
|
system = param @Text "system"
|
2026-03-29 10:38:50 +00:00
|
|
|
unless (system `elem` validSystems) do
|
|
|
|
|
setErrorMessage ("Invalid system: " <> system)
|
2026-04-12 12:17:45 +00:00
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
2026-03-29 10:38:50 +00:00
|
|
|
when (workItemRef == "") do
|
|
|
|
|
setErrorMessage "Work item reference cannot be empty"
|
2026-04-12 12:17:45 +00:00
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
2026-03-29 10:38:50 +00:00
|
|
|
newRecord @ImplementationChangeReference
|
|
|
|
|
|> set #decisionId decisionRecordId
|
|
|
|
|
|> set #workItemRef workItemRef
|
|
|
|
|
|> set #system system
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #linkedBy (fmap coerce linkedBy)
|
2026-03-29 10:38:50 +00:00
|
|
|
|> createRecord
|
|
|
|
|
setSuccessMessage "Implementation reference added"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
|
|
|
|
|
|
|
|
|
action DeleteImplementationRefAction { implementationChangeReferenceId } = do
|
|
|
|
|
ref <- fetch implementationChangeReferenceId
|
|
|
|
|
let decisionRecordId = ref.decisionId
|
|
|
|
|
deleteRecord ref
|
|
|
|
|
setSuccessMessage "Implementation reference removed"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
feat(P5): IHF Phase 5 complete — agent-assisted distillation
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>
2026-03-29 15:54:33 +00:00
|
|
|
|
feat(WP-0012): IHF Phase 11 — Advanced AI Federation
- Schema: AgentRegistration, ModelRoutingPolicy, AgentDelegation,
CollectiveProposal, CollectiveProposalContribution, AiGovernancePolicy,
AgentPerformanceRecord + ALTER TABLE agent_proposals
(migration 1744156800; CHECK constraints on trust_level, status,
consensus_status — GAAF compliant)
- Bridge: scripts/llm_bridge.py (llm-connect subprocess seam) +
Application/Helper/AgentBridge.hs (callBridge, callAgent,
checkGovernancePolicy, jsonArrayTexts)
- Routing: Application/Helper/ModelRouter.hs (resolveAgent,
resolveAllAgents) + ModelRoutingPolicies CRUD
- Registry: AgentRegistrations CRUD (Index/Show/New/Edit/Performance),
DeactivateAgentAction, ComputeAgentPerformanceAction
- Delegation: AgentDelegations controller + views, DelegateSubtaskAction
with token budget enforcement at bridge call time
- Collective: CollectiveProposals controller + views,
CreateCollectiveProposalAction (fan-out → synthesis → consensus detection)
- Governance: AiGovernancePolicies CRUD + ToggleAiGovernancePolicyAction;
checkGovernancePolicy enforced at all 4 Phase 5 invocation points
- Phase 5 wiring: replaced callClaudeApi in Widgets, DecisionRecords,
RequirementCandidates with resolveAgent + callAgent + token tracking
- llm-connect feature requests: ~/llm-connect/FEATURE_REQUESTS.md
(FR-1 HTTP serve, FR-2 RoutingPolicy, FR-3 async, FR-4 BudgetTracker)
- GAAF scorecard: 3.61 (up from 3.56); Functional 3.4→3.6, Extensions 3.8→3.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:57:17 +00:00
|
|
|
-- T07 / Phase 11: Propose implementation paths via routed agent
|
feat(P5): IHF Phase 5 complete — agent-assisted distillation
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>
2026-03-29 15:54:33 +00:00
|
|
|
action ProposeImplementationAction { decisionRecordId } = do
|
|
|
|
|
record <- fetch decisionRecordId
|
|
|
|
|
implRefs <- query @ImplementationChangeReference
|
|
|
|
|
|> filterWhere (#decisionId, decisionRecordId)
|
|
|
|
|
|> fetch
|
|
|
|
|
mRequirement <- case record.requirementId of
|
|
|
|
|
Nothing -> pure Nothing
|
|
|
|
|
Just rid -> fetchOneOrNothing rid
|
2026-04-29 10:46:50 +02:00
|
|
|
-- Resolve hub via the decision's linked candidate → source widget
|
|
|
|
|
mHubId <- case record.candidateId of
|
feat(WP-0012): IHF Phase 11 — Advanced AI Federation
- Schema: AgentRegistration, ModelRoutingPolicy, AgentDelegation,
CollectiveProposal, CollectiveProposalContribution, AiGovernancePolicy,
AgentPerformanceRecord + ALTER TABLE agent_proposals
(migration 1744156800; CHECK constraints on trust_level, status,
consensus_status — GAAF compliant)
- Bridge: scripts/llm_bridge.py (llm-connect subprocess seam) +
Application/Helper/AgentBridge.hs (callBridge, callAgent,
checkGovernancePolicy, jsonArrayTexts)
- Routing: Application/Helper/ModelRouter.hs (resolveAgent,
resolveAllAgents) + ModelRoutingPolicies CRUD
- Registry: AgentRegistrations CRUD (Index/Show/New/Edit/Performance),
DeactivateAgentAction, ComputeAgentPerformanceAction
- Delegation: AgentDelegations controller + views, DelegateSubtaskAction
with token budget enforcement at bridge call time
- Collective: CollectiveProposals controller + views,
CreateCollectiveProposalAction (fan-out → synthesis → consensus detection)
- Governance: AiGovernancePolicies CRUD + ToggleAiGovernancePolicyAction;
checkGovernancePolicy enforced at all 4 Phase 5 invocation points
- Phase 5 wiring: replaced callClaudeApi in Widgets, DecisionRecords,
RequirementCandidates with resolveAgent + callAgent + token tracking
- llm-connect feature requests: ~/llm-connect/FEATURE_REQUESTS.md
(FR-1 HTTP serve, FR-2 RoutingPolicy, FR-3 async, FR-4 BudgetTracker)
- GAAF scorecard: 3.61 (up from 3.56); Functional 3.4→3.6, Extensions 3.8→3.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:57:17 +00:00
|
|
|
Nothing -> pure Nothing
|
2026-04-29 10:46:50 +02:00
|
|
|
Just cid -> do
|
|
|
|
|
mCand <- fetchOneOrNothing cid
|
|
|
|
|
case mCand of
|
|
|
|
|
Nothing -> pure Nothing
|
|
|
|
|
Just cand -> fmap (.hubId) <$> fetchOneOrNothing cand.sourceWidgetId
|
feat(P5): IHF Phase 5 complete — agent-assisted distillation
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>
2026-03-29 15:54:33 +00:00
|
|
|
let implLines = map (\r -> r.system <> ": " <> r.workItemRef) implRefs
|
|
|
|
|
reqDesc = maybe "" (.description) mRequirement
|
|
|
|
|
userMsg = "Decision: " <> record.title
|
|
|
|
|
<> "\nRationale: " <> record.rationale
|
|
|
|
|
<> "\nOutcome: " <> record.outcome
|
|
|
|
|
<> "\nRequirement: " <> reqDesc
|
|
|
|
|
<> "\nExisting impl refs: " <> intercalate ", " implLines
|
feat(WP-0012): IHF Phase 11 — Advanced AI Federation
- Schema: AgentRegistration, ModelRoutingPolicy, AgentDelegation,
CollectiveProposal, CollectiveProposalContribution, AiGovernancePolicy,
AgentPerformanceRecord + ALTER TABLE agent_proposals
(migration 1744156800; CHECK constraints on trust_level, status,
consensus_status — GAAF compliant)
- Bridge: scripts/llm_bridge.py (llm-connect subprocess seam) +
Application/Helper/AgentBridge.hs (callBridge, callAgent,
checkGovernancePolicy, jsonArrayTexts)
- Routing: Application/Helper/ModelRouter.hs (resolveAgent,
resolveAllAgents) + ModelRoutingPolicies CRUD
- Registry: AgentRegistrations CRUD (Index/Show/New/Edit/Performance),
DeactivateAgentAction, ComputeAgentPerformanceAction
- Delegation: AgentDelegations controller + views, DelegateSubtaskAction
with token budget enforcement at bridge call time
- Collective: CollectiveProposals controller + views,
CreateCollectiveProposalAction (fan-out → synthesis → consensus detection)
- Governance: AiGovernancePolicies CRUD + ToggleAiGovernancePolicyAction;
checkGovernancePolicy enforced at all 4 Phase 5 invocation points
- Phase 5 wiring: replaced callClaudeApi in Widgets, DecisionRecords,
RequirementCandidates with resolveAgent + callAgent + token tracking
- llm-connect feature requests: ~/llm-connect/FEATURE_REQUESTS.md
(FR-1 HTTP serve, FR-2 RoutingPolicy, FR-3 async, FR-4 BudgetTracker)
- GAAF scorecard: 3.61 (up from 3.56); Functional 3.4→3.6, Extensions 3.8→3.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:57:17 +00:00
|
|
|
case mHubId of
|
|
|
|
|
Nothing -> do
|
|
|
|
|
setErrorMessage "Cannot determine hub for routing — ensure the decision has a linked requirement with a source widget"
|
feat(P5): IHF Phase 5 complete — agent-assisted distillation
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>
2026-03-29 15:54:33 +00:00
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
feat(WP-0012): IHF Phase 11 — Advanced AI Federation
- Schema: AgentRegistration, ModelRoutingPolicy, AgentDelegation,
CollectiveProposal, CollectiveProposalContribution, AiGovernancePolicy,
AgentPerformanceRecord + ALTER TABLE agent_proposals
(migration 1744156800; CHECK constraints on trust_level, status,
consensus_status — GAAF compliant)
- Bridge: scripts/llm_bridge.py (llm-connect subprocess seam) +
Application/Helper/AgentBridge.hs (callBridge, callAgent,
checkGovernancePolicy, jsonArrayTexts)
- Routing: Application/Helper/ModelRouter.hs (resolveAgent,
resolveAllAgents) + ModelRoutingPolicies CRUD
- Registry: AgentRegistrations CRUD (Index/Show/New/Edit/Performance),
DeactivateAgentAction, ComputeAgentPerformanceAction
- Delegation: AgentDelegations controller + views, DelegateSubtaskAction
with token budget enforcement at bridge call time
- Collective: CollectiveProposals controller + views,
CreateCollectiveProposalAction (fan-out → synthesis → consensus detection)
- Governance: AiGovernancePolicies CRUD + ToggleAiGovernancePolicyAction;
checkGovernancePolicy enforced at all 4 Phase 5 invocation points
- Phase 5 wiring: replaced callClaudeApi in Widgets, DecisionRecords,
RequirementCandidates with resolveAgent + callAgent + token tracking
- llm-connect feature requests: ~/llm-connect/FEATURE_REQUESTS.md
(FR-1 HTTP serve, FR-2 RoutingPolicy, FR-3 async, FR-4 BudgetTracker)
- GAAF scorecard: 3.61 (up from 3.56); Functional 3.4→3.6, Extensions 3.8→3.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:57:17 +00:00
|
|
|
Just hubId -> do
|
|
|
|
|
mAgent <- resolveAgent hubId "implementation"
|
|
|
|
|
case mAgent of
|
|
|
|
|
Nothing -> do
|
|
|
|
|
setErrorMessage "No routing policy for 'implementation' task type"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
|
|
|
|
Just agent -> do
|
|
|
|
|
allowed <- checkGovernancePolicy hubId agent.id "decision_record"
|
|
|
|
|
if not allowed
|
|
|
|
|
then do
|
|
|
|
|
newRecord @AgentProposal
|
|
|
|
|
|> set #proposalType "impl_proposal"
|
|
|
|
|
|> set #sourceDecisionId (Just decisionRecordId)
|
|
|
|
|
|> set #content "Blocked by AI governance policy"
|
|
|
|
|
|> set #modelRef agent.modelName
|
|
|
|
|
|> set #status "blocked_by_policy"
|
|
|
|
|
|> set #agentRegistrationId (Just agent.id)
|
|
|
|
|
|> createRecord
|
|
|
|
|
setErrorMessage "Blocked by AI governance policy"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
|
|
|
|
else do
|
|
|
|
|
result <- liftIO $ callAgent agent userMsg
|
|
|
|
|
case result of
|
|
|
|
|
Left err -> do
|
2026-04-01 22:48:29 +00:00
|
|
|
setErrorMessage ("Implementation proposal failed: " <> bridgeErrorMessage err)
|
feat(WP-0012): IHF Phase 11 — Advanced AI Federation
- Schema: AgentRegistration, ModelRoutingPolicy, AgentDelegation,
CollectiveProposal, CollectiveProposalContribution, AiGovernancePolicy,
AgentPerformanceRecord + ALTER TABLE agent_proposals
(migration 1744156800; CHECK constraints on trust_level, status,
consensus_status — GAAF compliant)
- Bridge: scripts/llm_bridge.py (llm-connect subprocess seam) +
Application/Helper/AgentBridge.hs (callBridge, callAgent,
checkGovernancePolicy, jsonArrayTexts)
- Routing: Application/Helper/ModelRouter.hs (resolveAgent,
resolveAllAgents) + ModelRoutingPolicies CRUD
- Registry: AgentRegistrations CRUD (Index/Show/New/Edit/Performance),
DeactivateAgentAction, ComputeAgentPerformanceAction
- Delegation: AgentDelegations controller + views, DelegateSubtaskAction
with token budget enforcement at bridge call time
- Collective: CollectiveProposals controller + views,
CreateCollectiveProposalAction (fan-out → synthesis → consensus detection)
- Governance: AiGovernancePolicies CRUD + ToggleAiGovernancePolicyAction;
checkGovernancePolicy enforced at all 4 Phase 5 invocation points
- Phase 5 wiring: replaced callClaudeApi in Widgets, DecisionRecords,
RequirementCandidates with resolveAgent + callAgent + token tracking
- llm-connect feature requests: ~/llm-connect/FEATURE_REQUESTS.md
(FR-1 HTTP serve, FR-2 RoutingPolicy, FR-3 async, FR-4 BudgetTracker)
- GAAF scorecard: 3.61 (up from 3.56); Functional 3.4→3.6, Extensions 3.8→3.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:57:17 +00:00
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
|
|
|
|
Right resp -> do
|
|
|
|
|
newRecord @AgentProposal
|
|
|
|
|
|> set #proposalType "impl_proposal"
|
|
|
|
|
|> set #sourceDecisionId (Just decisionRecordId)
|
|
|
|
|
|> set #content resp.content
|
|
|
|
|
|> set #modelRef resp.modelUsed
|
|
|
|
|
|> set #status "pending"
|
|
|
|
|
|> set #agentRegistrationId (Just agent.id)
|
|
|
|
|
|> set #tokensIn (Just resp.tokensIn)
|
|
|
|
|
|> set #tokensOut (Just resp.tokensOut)
|
|
|
|
|
|> createRecord
|
|
|
|
|
setSuccessMessage "Implementation proposal created"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId }
|
feat(WP-0013): IHF Phase 12 — Platform Memory and Continuous Learning
Closes the long-range feedback loop: outcome signals now enrich the full
traceability chain and feed back into routing, triage, and AI proposals.
Schema (T01):
- outcome_correlations (CHECK correlation_type)
- pattern_performance_records
- adaptive_threshold_configs
- institutional_knowledge_entries (GIN tsvector FTS)
- learning_insights (CHECK insight_type)
- ALTER TABLE decision_records + requirement_candidates: outcome_summary JSONB
- AFTER INSERT trigger trg_enrich_lineage on outcome_signals
- contracts/core/ updated (outcome-summary-columns-v1, append-only addendum)
Correlation engine (T02):
- Application/Helper/CorrelationEngine.hs: pure annotation→outcome SQL
- Web/Controller/OutcomeCorrelations.hs: ComputeCorrelationsAction + index
Pattern performance (T03):
- Web/Controller/PatternPerformance.hs: ComputePatternPerformanceAction
Adaptive thresholds (T04):
- Web/Controller/AdaptiveThresholds.hs: CalibrateThresholdsAction
- Application/Helper/FrictionScore.hs: applyAdaptiveWeights
Institutional knowledge (T05):
- DistilDecisionAction in DecisionRecords controller
- Web/Controller/InstitutionalKnowledge.hs: QueryKnowledgeBaseAction
Lineage enrichment (T06):
- Web/Controller/LineageEnrichment.hs: EnrichLineageAction (batch backfill)
- enrich_lineage_on_outcome_batch() PL/pgSQL helper in migration
Learning dashboard (T07):
- Web/Controller/LearningDashboard.hs: 5-panel autoRefresh view
- "Learning" nav link in FrontController
API v2 learning endpoints (T08):
- GET /api/v2/outcome-correlations, /pattern-performance, /knowledge-base/{id}
- OpenAPI schemas: OutcomeCorrelation, PatternPerformanceRecord, InstitutionalKnowledgeEntry
GAAF scorecard + docs (T09):
- Core 3.8→3.9, Functional 3.6→3.8, overall 3.61→3.68
- CLAUDE.md: IHF v0.2 complete, no active workplan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:14:15 +00:00
|
|
|
|