feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
module Web.Controller.RequirementCandidates where
|
|
|
|
|
|
|
|
|
|
import Web.Types
|
|
|
|
|
import Web.View.RequirementCandidates.Index
|
|
|
|
|
import Web.View.RequirementCandidates.Show
|
|
|
|
|
import Web.View.RequirementCandidates.New
|
|
|
|
|
import Web.View.RequirementCandidates.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(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
|
|
|
import Data.Aeson (decode, Value(..), Array)
|
|
|
|
|
import Data.Aeson.Lens (key, _String)
|
|
|
|
|
import Control.Lens ((^?))
|
|
|
|
|
import Data.ByteString.Lazy (fromStrict)
|
feat(WP-0010): IHF Phase 9 — External API Surface and Consumer SDKs
Delivers the full Phase 9 external API layer:
- Versioned REST API (/api/v2/) with OpenAPI 3.1 spec; enum arrays for
widget_type, event_type, annotation category drawn live from registry tables
- OAuth 2.0 client credentials flow (/api/v2/token); hub:*:write scopes
gated on active HubCapabilityManifest FK
- API key management: SHA256-hashed tokens, key_prefix for display,
one-time reveal on creation, revocation support
- TypeScript and Python consumer SDKs generated from registry tables
(/api/v2/sdk/ihf-client.ts, /api/v2/sdk/ihf-client.py)
- Webhook delivery: HMAC-SHA256 signing, append-only webhook_deliveries,
fire-and-forget dispatch via forkIO, 3-retry logic
- Admin API dashboard with 24h stats (request count, error rate, last seen)
- Rate limiting (per-minute) and daily quota enforcement via api_request_log
- Schema migration: api_consumers, api_keys, webhook_subscriptions (CHECK
constraint on 6 framework lifecycle topics), webhook_deliveries
(append-only trigger), api_request_log
- ARCHITECTURE-LAYERS.md scorecard: 3.34 → 3.41 (approaching Strong)
- contracts/functional/interaction-reporting-v1.md extended with Phase 9
endpoint catalogue and 422 validation error format
GAAF: no bare TEXT discriminators; webhook event_type uses CHECK constraint
over 6 allowed framework lifecycle topic strings (not widget event types).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 19:52:20 +00:00
|
|
|
import Web.Job.WebhookDeliveryJob (dispatchWebhooks)
|
|
|
|
|
import Control.Concurrent (forkIO)
|
2026-04-12 12:17:45 +00:00
|
|
|
import Control.Monad (void)
|
|
|
|
|
import Data.Coerce (coerce)
|
2026-04-29 10:46:50 +02:00
|
|
|
import Data.Scientific (Scientific)
|
feat(WP-0010): IHF Phase 9 — External API Surface and Consumer SDKs
Delivers the full Phase 9 external API layer:
- Versioned REST API (/api/v2/) with OpenAPI 3.1 spec; enum arrays for
widget_type, event_type, annotation category drawn live from registry tables
- OAuth 2.0 client credentials flow (/api/v2/token); hub:*:write scopes
gated on active HubCapabilityManifest FK
- API key management: SHA256-hashed tokens, key_prefix for display,
one-time reveal on creation, revocation support
- TypeScript and Python consumer SDKs generated from registry tables
(/api/v2/sdk/ihf-client.ts, /api/v2/sdk/ihf-client.py)
- Webhook delivery: HMAC-SHA256 signing, append-only webhook_deliveries,
fire-and-forget dispatch via forkIO, 3-retry logic
- Admin API dashboard with 24h stats (request count, error rate, last seen)
- Rate limiting (per-minute) and daily quota enforcement via api_request_log
- Schema migration: api_consumers, api_keys, webhook_subscriptions (CHECK
constraint on 6 framework lifecycle topics), webhook_deliveries
(append-only trigger), api_request_log
- ARCHITECTURE-LAYERS.md scorecard: 3.34 → 3.41 (approaching Strong)
- contracts/functional/interaction-reporting-v1.md extended with Phase 9
endpoint catalogue and 422 validation error format
GAAF: no bare TEXT discriminators; webhook event_type uses CHECK constraint
over 6 allowed framework lifecycle topic strings (not widget event types).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 19:52:20 +00:00
|
|
|
import Data.Aeson ((.=), object)
|
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
|
|
|
import Data.Text.Encoding (encodeUtf8)
|
|
|
|
|
import Data.HashMap.Strict (HashMap)
|
|
|
|
|
import qualified Data.HashMap.Strict as HashMap
|
|
|
|
|
import qualified Data.Vector as Vector
|
|
|
|
|
import Control.Monad (forM_)
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|
|
|
|
|
validStatuses :: [Text]
|
|
|
|
|
validStatuses = ["open", "in_review", "accepted", "rejected", "deferred"]
|
|
|
|
|
|
|
|
|
|
validCategories :: [Text]
|
|
|
|
|
validCategories = ["friction", "defect", "wish", "policy_concern", "doc_gap", "trust", "other"]
|
|
|
|
|
|
|
|
|
|
-- Allowed triage transitions
|
|
|
|
|
allowedTransition :: Text -> Text -> Bool
|
|
|
|
|
allowedTransition "open" "in_review" = True
|
|
|
|
|
allowedTransition "in_review" "accepted" = True
|
|
|
|
|
allowedTransition "in_review" "rejected" = True
|
|
|
|
|
allowedTransition "in_review" "deferred" = True
|
|
|
|
|
allowedTransition "deferred" "in_review" = True
|
|
|
|
|
allowedTransition _ _ = False
|
|
|
|
|
|
|
|
|
|
instance Controller RequirementCandidatesController where
|
|
|
|
|
beforeAction = ensureIsUser
|
|
|
|
|
|
|
|
|
|
action RequirementCandidatesAction = 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 mStatusFilter = paramOrNothing @Text "status"
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
candidates <- case mStatusFilter of
|
|
|
|
|
Nothing -> query @RequirementCandidate |> orderByDesc #createdAt |> fetch
|
|
|
|
|
Just s -> query @RequirementCandidate
|
|
|
|
|
|> filterWhere (#status, s)
|
|
|
|
|
|> orderByDesc #createdAt
|
|
|
|
|
|> fetch
|
|
|
|
|
-- Fetch reviewer assignments for display
|
|
|
|
|
assignments <- query @ReviewerAssignment |> fetch
|
|
|
|
|
users <- query @User |> fetch
|
|
|
|
|
widgets <- query @Widget |> fetch
|
|
|
|
|
render IndexView { candidates, assignments, users, widgets, mStatusFilter }
|
|
|
|
|
|
|
|
|
|
action ShowRequirementCandidateAction { requirementCandidateId } = do
|
|
|
|
|
candidate <- fetch requirementCandidateId
|
|
|
|
|
widget <- fetch candidate.sourceWidgetId
|
|
|
|
|
triageStates <- query @TriageState
|
|
|
|
|
|> filterWhere (#candidateId, requirementCandidateId)
|
|
|
|
|
|> orderByAsc #changedAt
|
|
|
|
|
|> fetch
|
|
|
|
|
mAssignment <- query @ReviewerAssignment
|
|
|
|
|
|> filterWhere (#candidateId, requirementCandidateId)
|
|
|
|
|
|> fetchOneOrNothing
|
|
|
|
|
users <- query @User |> fetch
|
|
|
|
|
mSourceAnnotation <- case candidate.sourceAnnotationId of
|
|
|
|
|
Nothing -> pure Nothing
|
|
|
|
|
Just aid -> fetchOneOrNothing aid
|
|
|
|
|
mSourceThread <- case candidate.sourceThreadId of
|
|
|
|
|
Nothing -> pure Nothing
|
|
|
|
|
Just tid -> fetchOneOrNothing tid
|
|
|
|
|
render ShowView { candidate, widget, triageStates, mAssignment, users, mSourceAnnotation, mSourceThread }
|
|
|
|
|
|
|
|
|
|
action NewRequirementCandidateAction = do
|
|
|
|
|
widgets <- query @Widget |> fetch
|
|
|
|
|
threads <- query @AnnotationThread |> fetch
|
|
|
|
|
let candidate = newRecord @RequirementCandidate
|
|
|
|
|
render NewView { candidate, widgets, threads }
|
|
|
|
|
|
|
|
|
|
action CreateRequirementCandidateAction = do
|
|
|
|
|
widgets <- query @Widget |> fetch
|
|
|
|
|
threads <- query @AnnotationThread |> 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
|
|
|
|
|
createdBy = fmap (.id) mUser
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|
|
|
|
|
let candidate = newRecord @RequirementCandidate
|
|
|
|
|
candidate
|
|
|
|
|
|> fill @'["title", "description", "sourceWidgetId", "sourceThreadId", "category"]
|
|
|
|
|
|> set #status "open"
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #createdBy (fmap coerce createdBy)
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|> validateField #title nonEmpty
|
|
|
|
|
|> validateField #description nonEmpty
|
2026-04-29 10:46:50 +02:00
|
|
|
|> validateField #category (isInList validCategories)
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|> ifValid \case
|
|
|
|
|
Left candidate -> render NewView { candidate, widgets, threads }
|
|
|
|
|
Right candidate -> do
|
|
|
|
|
created <- createRecord candidate
|
feat(WP-0010): IHF Phase 9 — External API Surface and Consumer SDKs
Delivers the full Phase 9 external API layer:
- Versioned REST API (/api/v2/) with OpenAPI 3.1 spec; enum arrays for
widget_type, event_type, annotation category drawn live from registry tables
- OAuth 2.0 client credentials flow (/api/v2/token); hub:*:write scopes
gated on active HubCapabilityManifest FK
- API key management: SHA256-hashed tokens, key_prefix for display,
one-time reveal on creation, revocation support
- TypeScript and Python consumer SDKs generated from registry tables
(/api/v2/sdk/ihf-client.ts, /api/v2/sdk/ihf-client.py)
- Webhook delivery: HMAC-SHA256 signing, append-only webhook_deliveries,
fire-and-forget dispatch via forkIO, 3-retry logic
- Admin API dashboard with 24h stats (request count, error rate, last seen)
- Rate limiting (per-minute) and daily quota enforcement via api_request_log
- Schema migration: api_consumers, api_keys, webhook_subscriptions (CHECK
constraint on 6 framework lifecycle topics), webhook_deliveries
(append-only trigger), api_request_log
- ARCHITECTURE-LAYERS.md scorecard: 3.34 → 3.41 (approaching Strong)
- contracts/functional/interaction-reporting-v1.md extended with Phase 9
endpoint catalogue and 422 validation error format
GAAF: no bare TEXT discriminators; webhook event_type uses CHECK constraint
over 6 allowed framework lifecycle topic strings (not widget event types).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 19:52:20 +00:00
|
|
|
-- Dispatch webhooks fire-and-forget
|
|
|
|
|
let webhookPayload = object
|
|
|
|
|
[ "event" .= ("requirement_candidate.created" :: Text)
|
|
|
|
|
, "resourceId" .= created.id
|
|
|
|
|
, "title" .= created.title
|
|
|
|
|
, "category" .= created.category
|
|
|
|
|
]
|
|
|
|
|
liftIO $ void $ forkIO $
|
|
|
|
|
dispatchWebhooks "requirement_candidate.created" webhookPayload
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
setSuccessMessage "Requirement candidate created"
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId = created.id }
|
|
|
|
|
|
|
|
|
|
action EditRequirementCandidateAction { requirementCandidateId } = do
|
|
|
|
|
candidate <- fetch requirementCandidateId
|
|
|
|
|
widgets <- query @Widget |> fetch
|
|
|
|
|
threads <- query @AnnotationThread |> fetch
|
|
|
|
|
render EditView { candidate, widgets, threads }
|
|
|
|
|
|
|
|
|
|
action UpdateRequirementCandidateAction { requirementCandidateId } = do
|
|
|
|
|
candidate <- fetch requirementCandidateId
|
|
|
|
|
widgets <- query @Widget |> fetch
|
|
|
|
|
threads <- query @AnnotationThread |> fetch
|
|
|
|
|
|
|
|
|
|
candidate
|
|
|
|
|
|> fill @'["title", "description", "sourceWidgetId", "sourceThreadId", "category"]
|
|
|
|
|
|> validateField #title nonEmpty
|
|
|
|
|
|> validateField #description nonEmpty
|
2026-04-29 10:46:50 +02:00
|
|
|
|> validateField #category (isInList validCategories)
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|> ifValid \case
|
|
|
|
|
Left candidate -> render EditView { candidate, widgets, threads }
|
|
|
|
|
Right candidate -> do
|
|
|
|
|
updateRecord candidate
|
|
|
|
|
setSuccessMessage "Candidate updated"
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
|
|
|
|
|
action UpdateTriageStatusAction { requirementCandidateId } = do
|
|
|
|
|
candidate <- fetch requirementCandidateId
|
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 newStatus = param @Text "status"
|
|
|
|
|
notes = paramOrNothing @Text "notes"
|
|
|
|
|
mUser = currentUserOrNothing
|
|
|
|
|
changedBy = fmap (.id) mUser
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|
|
|
|
|
if allowedTransition candidate.status newStatus
|
|
|
|
|
then do
|
|
|
|
|
-- Insert triage state row (append-only audit trail)
|
|
|
|
|
newRecord @TriageState
|
|
|
|
|
|> set #candidateId requirementCandidateId
|
|
|
|
|
|> set #status newStatus
|
|
|
|
|
|> set #notes notes
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #changedBy (fmap coerce changedBy)
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|> createRecord
|
|
|
|
|
-- Update current status on candidate
|
|
|
|
|
candidate
|
|
|
|
|
|> set #status newStatus
|
|
|
|
|
|> updateRecord
|
|
|
|
|
setSuccessMessage ("Status updated to " <> newStatus)
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
else do
|
|
|
|
|
setErrorMessage ("Invalid transition: " <> candidate.status <> " → " <> newStatus)
|
2026-04-12 12:17:45 +00:00
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|
|
|
|
|
action AssignReviewerAction { requirementCandidateId } = 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 userId = param @(Id User) "userId"
|
|
|
|
|
mUser = currentUserOrNothing
|
|
|
|
|
assignedBy = fmap (.id) mUser
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|
|
|
|
|
-- Upsert: delete existing assignment then insert
|
|
|
|
|
existing <- query @ReviewerAssignment
|
|
|
|
|
|> filterWhere (#candidateId, requirementCandidateId)
|
|
|
|
|
|> fetchOneOrNothing
|
|
|
|
|
case existing of
|
|
|
|
|
Just ra -> deleteRecord ra
|
|
|
|
|
Nothing -> pure ()
|
|
|
|
|
|
|
|
|
|
newRecord @ReviewerAssignment
|
|
|
|
|
|> set #candidateId requirementCandidateId
|
|
|
|
|
|> set #userId userId
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #assignedBy (fmap coerce assignedBy)
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
|> createRecord
|
|
|
|
|
|
|
|
|
|
setSuccessMessage "Reviewer assigned"
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
|
|
|
|
|
action MyQueueAction = 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
|
feat(P2+P3): IHF Phase 2 complete; register Phase 3 workplan
Phase 2 — Structured Feedback and Triage (IHUB-WP-0002):
- Schema: annotation_threads, requirement_candidates, triage_states,
reviewer_assignments; annotations extended with severity + thread_id
- AnnotationThreadsController: create threads, assign annotations
- RequirementCandidatesController: CRUD, escalation, triage lifecycle,
reviewer assignment, my-queue
- Annotation severity (low/medium/high/critical) with Tailwind color cues
- TriageDashboardAction on HubsController with autoRefresh
- Integration tests (T01–T09), SCOPE.md updated, docs/phase2-summary.md
Phase 3 — Governance and Decision Linkage (IHUB-WP-0003):
- Workplan registered: 9 tasks, State Hub workstream 5f201ee3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 23:37:34 +00:00
|
|
|
case mUser of
|
|
|
|
|
Nothing -> redirectTo RequirementCandidatesAction
|
|
|
|
|
Just user -> do
|
|
|
|
|
assignments <- query @ReviewerAssignment
|
|
|
|
|
|> filterWhere (#userId, user.id)
|
|
|
|
|
|> fetch
|
|
|
|
|
let candidateIds = map (.candidateId) assignments
|
|
|
|
|
candidates <- mapM fetch candidateIds
|
|
|
|
|
let active = filter (\c -> c.status `elem` ["open", "in_review"]) candidates
|
|
|
|
|
widgets <- query @Widget |> fetch
|
|
|
|
|
render IndexView
|
|
|
|
|
{ candidates = active
|
|
|
|
|
, assignments
|
|
|
|
|
, users = [user]
|
|
|
|
|
, widgets
|
|
|
|
|
, mStatusFilter = Just "my_queue"
|
|
|
|
|
}
|
2026-03-29 10:38:50 +00:00
|
|
|
|
|
|
|
|
action PromoteToRequirementAction { requirementCandidateId } = do
|
|
|
|
|
candidate <- fetch requirementCandidateId
|
|
|
|
|
-- Guard: only accepted candidates may be promoted
|
|
|
|
|
when (candidate.status /= "accepted") do
|
|
|
|
|
setErrorMessage "Only accepted candidates can be promoted to a requirement"
|
2026-04-12 12:17:45 +00:00
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
2026-03-29 10:38:50 +00:00
|
|
|
-- Idempotent: if already promoted, redirect to existing requirement
|
|
|
|
|
case candidate.requirementId of
|
|
|
|
|
Just rid -> redirectTo ShowRequirementAction { requirementId = rid }
|
|
|
|
|
Nothing -> 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
|
2026-03-29 10:38:50 +00:00
|
|
|
req <- newRecord @Requirement
|
|
|
|
|
|> set #title candidate.title
|
|
|
|
|
|> set #description candidate.description
|
|
|
|
|
|> set #sourceCandidateId requirementCandidateId
|
|
|
|
|
|> set #status "active"
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #createdBy (fmap coerce createdBy)
|
2026-03-29 10:38:50 +00:00
|
|
|
|> createRecord
|
|
|
|
|
candidate
|
|
|
|
|
|> set #requirementId (Just req.id)
|
|
|
|
|
|> updateRecord
|
|
|
|
|
setSuccessMessage "Promoted to requirement"
|
|
|
|
|
redirectTo ShowRequirementAction { requirementId = req.id }
|
|
|
|
|
|
|
|
|
|
action LinkToDecisionAction { requirementCandidateId } = do
|
|
|
|
|
candidate <- fetch requirementCandidateId
|
|
|
|
|
-- Guard: only accepted candidates
|
|
|
|
|
when (candidate.status /= "accepted") do
|
|
|
|
|
setErrorMessage "Only accepted candidates can be linked to a decision"
|
2026-04-12 12:17:45 +00:00
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
2026-03-29 10:38:50 +00:00
|
|
|
-- Idempotent: check if a decision already links to this candidate
|
|
|
|
|
existing <- query @DecisionRecord
|
|
|
|
|
|> filterWhere (#candidateId, Just requirementCandidateId)
|
|
|
|
|
|> fetchOneOrNothing
|
|
|
|
|
case existing of
|
|
|
|
|
Just dr -> redirectTo ShowDecisionRecordAction { decisionRecordId = dr.id }
|
|
|
|
|
Nothing -> 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
|
|
|
|
|
decidedBy = fmap (.id) mUser
|
2026-03-29 10:38:50 +00:00
|
|
|
-- Use promoted requirement id if available
|
|
|
|
|
let mReqId = candidate.requirementId
|
|
|
|
|
dr <- newRecord @DecisionRecord
|
|
|
|
|
|> set #title candidate.title
|
|
|
|
|
|> set #rationale candidate.description
|
|
|
|
|
|> set #outcome "accepted"
|
|
|
|
|
|> set #candidateId (Just requirementCandidateId)
|
|
|
|
|
|> set #requirementId mReqId
|
2026-04-12 12:17:45 +00:00
|
|
|
|> set #decidedBy (fmap coerce decidedBy)
|
2026-03-29 10:38:50 +00:00
|
|
|
|> createRecord
|
|
|
|
|
setSuccessMessage "Decision record created"
|
|
|
|
|
redirectTo ShowDecisionRecordAction { decisionRecordId = dr.id }
|
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
|
|
|
-- T05 / Phase 11: Detect duplicate candidates 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 DetectDuplicatesAction { requirementCandidateId } = do
|
|
|
|
|
target <- fetch requirementCandidateId
|
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
|
|
|
others <- query @RequirementCandidate |> fetch
|
2026-04-29 10:46:50 +02:00
|
|
|
-- Resolve hub from the source widget (sourceWidgetId is non-nullable)
|
|
|
|
|
mHubId <- fmap (.hubId) <$> fetchOneOrNothing target.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 otherLines = map (\c -> show c.id <> " | " <> c.title <> ": " <> c.description)
|
|
|
|
|
(filter (\c -> c.id /= requirementCandidateId) others)
|
|
|
|
|
targetLine = "TARGET: " <> target.title <> ": " <> target.description
|
|
|
|
|
userMsg = targetLine <> "\n\nEXISTING:\n" <> intercalate "\n" otherLines
|
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 candidate has 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 ShowRequirementCandidateAction { requirementCandidateId }
|
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 "triage"
|
|
|
|
|
case mAgent of
|
|
|
|
|
Nothing -> do
|
|
|
|
|
setErrorMessage "No routing policy for 'triage' task type"
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
Just agent -> do
|
|
|
|
|
allowed <- checkGovernancePolicy hubId agent.id "requirement_candidate"
|
|
|
|
|
if not allowed
|
|
|
|
|
then do
|
|
|
|
|
newRecord @AgentProposal
|
|
|
|
|
|> set #proposalType "duplicate_flag"
|
|
|
|
|
|> set #sourceCandidateId (Just requirementCandidateId)
|
|
|
|
|
|> 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 ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
else do
|
|
|
|
|
result <- liftIO $ callAgent agent userMsg
|
|
|
|
|
case result of
|
|
|
|
|
Left err -> do
|
2026-04-01 22:48:29 +00:00
|
|
|
setErrorMessage ("Duplicate detection 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 ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
Right resp -> do
|
|
|
|
|
newRecord @AgentProposal
|
|
|
|
|
|> set #proposalType "duplicate_flag"
|
|
|
|
|
|> set #sourceCandidateId (Just requirementCandidateId)
|
|
|
|
|
|> 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 "Duplicate detection proposal created"
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
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
|
|
|
|
2026-04-01 23:22:15 +00:00
|
|
|
-- T06: Detect policy sensitivity 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 DetectPolicySensitivityAction { requirementCandidateId } = do
|
|
|
|
|
candidate <- fetch requirementCandidateId
|
2026-04-29 10:46:50 +02:00
|
|
|
-- sourceWidgetId is non-nullable; fetchOneOrNothing handles missing widget
|
|
|
|
|
mWidget <- fetchOneOrNothing candidate.sourceWidgetId
|
|
|
|
|
mHubId <- fmap (.hubId) <$> fetchOneOrNothing candidate.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 policyCtx = maybe "unknown" (.policyScope) mWidget
|
|
|
|
|
userMsg = "Title: " <> candidate.title
|
|
|
|
|
<> "\nDescription: " <> candidate.description
|
|
|
|
|
<> "\nPolicy scope context: " <> policyCtx
|
2026-04-01 23:22:15 +00:00
|
|
|
<> "\nRespond with JSON: {\"concerns\": [{\"scope\": \"...\", \"note\": \"...\"}], \"severity\": \"low|medium|high\"}."
|
|
|
|
|
case mHubId of
|
|
|
|
|
Nothing -> do
|
|
|
|
|
setErrorMessage "Cannot determine hub for routing — ensure the candidate has 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 ShowRequirementCandidateAction { requirementCandidateId }
|
2026-04-01 23:22:15 +00:00
|
|
|
Just hubId -> do
|
|
|
|
|
mAgent <- resolveAgent hubId "policy_sensitivity"
|
|
|
|
|
case mAgent of
|
|
|
|
|
Nothing -> do
|
|
|
|
|
setErrorMessage "No routing policy for 'policy_sensitivity' task type"
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
Just agent -> do
|
|
|
|
|
allowed <- checkGovernancePolicy hubId agent.id "requirement_candidate"
|
|
|
|
|
if not allowed
|
|
|
|
|
then do
|
|
|
|
|
newRecord @AgentProposal
|
|
|
|
|
|> set #proposalType "policy_flag"
|
|
|
|
|
|> set #sourceCandidateId (Just requirementCandidateId)
|
|
|
|
|
|> 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 ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
else do
|
|
|
|
|
result <- liftIO $ callAgent agent userMsg
|
|
|
|
|
case result of
|
|
|
|
|
Left err -> do
|
|
|
|
|
setErrorMessage ("Policy check failed: " <> bridgeErrorMessage err)
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
|
|
|
|
Right resp -> do
|
2026-04-29 10:46:50 +02:00
|
|
|
let confidenceScore = realToFrac (extractSeverityScore resp.content) :: Scientific
|
2026-04-01 23:22:15 +00:00
|
|
|
proposal <- newRecord @AgentProposal
|
|
|
|
|
|> set #proposalType "policy_flag"
|
|
|
|
|
|> set #sourceCandidateId (Just requirementCandidateId)
|
|
|
|
|
|> set #content resp.content
|
|
|
|
|
|> set #modelRef resp.modelUsed
|
|
|
|
|
|> set #confidence (Just confidenceScore)
|
|
|
|
|
|> set #status "pending"
|
|
|
|
|
|> set #agentRegistrationId (Just agent.id)
|
|
|
|
|
|> set #tokensIn (Just resp.tokensIn)
|
|
|
|
|
|> set #tokensOut (Just resp.tokensOut)
|
|
|
|
|
|> createRecord
|
|
|
|
|
-- Create one ConfidenceAnnotation per concern scope
|
|
|
|
|
let mParsed = decode (fromStrict (encodeUtf8 resp.content))
|
|
|
|
|
:: Maybe (HashMap Text Value)
|
|
|
|
|
case mParsed >>= HashMap.lookup "concerns" of
|
|
|
|
|
Just (Array concerns) ->
|
|
|
|
|
forM_ (Vector.toList concerns) \concern ->
|
|
|
|
|
case (concern ^? key "scope" . _String
|
|
|
|
|
,concern ^? key "note" . _String) of
|
|
|
|
|
(Just scope, noteM) ->
|
2026-04-29 10:46:50 +02:00
|
|
|
void $ newRecord @ConfidenceAnnotation
|
2026-04-01 23:22:15 +00:00
|
|
|
|> set #proposalId proposal.id
|
|
|
|
|
|> set #dimension scope
|
2026-04-29 10:46:50 +02:00
|
|
|
|> set #score (confidenceScore :: Scientific)
|
2026-04-01 23:22:15 +00:00
|
|
|
|> set #explanation noteM
|
|
|
|
|
|> createRecord
|
|
|
|
|
_ -> pure ()
|
|
|
|
|
_ -> pure ()
|
|
|
|
|
setSuccessMessage "Policy check proposal created"
|
|
|
|
|
redirectTo ShowRequirementCandidateAction { requirementCandidateId }
|
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
|
|
|
|
|
|
|
|
-- Map severity string to numeric confidence
|
|
|
|
|
extractSeverityScore :: Text -> Double
|
|
|
|
|
extractSeverityScore content
|
|
|
|
|
| "\"high\"" `isInfixOf` content = 0.9
|
|
|
|
|
| "\"medium\"" `isInfixOf` content = 0.6
|
|
|
|
|
| otherwise = 0.3
|