generated from coulomb/repo-seed
feat(P3): IHF Phase 3 complete — Governance and Decision Linkage
Implements the full governance layer: - Schema: requirements, decision_records, policy_references, implementation_change_references; requirement_candidates gets requirement_id back-reference - RequirementsController (index/show; promotion-only create) - DecisionRecordsController (CRUD + policy/impl ref management) - GovernanceDashboardAction on HubsController (AutoRefresh) - PromoteToRequirementAction + LinkToDecisionAction on candidates - Outcome immutability enforced at controller level (fill excludes outcome) - Full six-outcome vocabulary with Tailwind color roles - Integration tests for all Phase 3 paths - FrontController: registers Phase 2 missing controllers + all Phase 3 - SCOPE.md + docs/phase3-summary.md updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,9 @@ instance View ShowView where
|
||||
{renderReviewerSection candidate mAssignment users}
|
||||
</div>
|
||||
|
||||
<!-- Phase 3: Promote to Requirement / Link to Decision -->
|
||||
{renderGovernanceActions candidate}
|
||||
|
||||
<!-- Triage history -->
|
||||
<div class="bg-white rounded-lg border border-gray-200 px-6 py-4">
|
||||
<h2 class="text-sm font-semibold text-gray-700 mb-3">Triage History</h2>
|
||||
@@ -167,6 +170,51 @@ renderTriageRow ts = [hsx|
|
||||
</li>
|
||||
|]
|
||||
|
||||
renderGovernanceActions :: RequirementCandidate -> Html
|
||||
renderGovernanceActions candidate
|
||||
| candidate.status == "accepted" = [hsx|
|
||||
<div class="bg-white rounded-lg border border-indigo-200 px-6 py-4">
|
||||
<h2 class="text-sm font-semibold text-gray-700 mb-3">Governance</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
{renderPromoteButton candidate}
|
||||
{renderLinkDecisionButton candidate}
|
||||
</div>
|
||||
</div>
|
||||
|]
|
||||
| otherwise = mempty
|
||||
|
||||
renderPromoteButton :: RequirementCandidate -> Html
|
||||
renderPromoteButton candidate =
|
||||
case candidate.requirementId of
|
||||
Just rid -> [hsx|
|
||||
<a href={ShowRequirementAction { requirementId = rid }}
|
||||
class="text-sm text-green-700 bg-green-50 border border-green-200 px-3 py-1.5 rounded hover:bg-green-100">
|
||||
Requirement →
|
||||
</a>
|
||||
|]
|
||||
Nothing -> [hsx|
|
||||
<form method="POST"
|
||||
action={PromoteToRequirementAction { requirementCandidateId = candidate.id }}>
|
||||
{hiddenField "authenticity_token"}
|
||||
<button type="submit"
|
||||
class="text-sm bg-indigo-600 text-white px-3 py-1.5 rounded hover:bg-indigo-700">
|
||||
Promote to Requirement
|
||||
</button>
|
||||
</form>
|
||||
|]
|
||||
|
||||
renderLinkDecisionButton :: RequirementCandidate -> Html
|
||||
renderLinkDecisionButton candidate = [hsx|
|
||||
<form method="POST"
|
||||
action={LinkToDecisionAction { requirementCandidateId = candidate.id }}>
|
||||
{hiddenField "authenticity_token"}
|
||||
<button type="submit"
|
||||
class="text-sm bg-gray-700 text-white px-3 py-1.5 rounded hover:bg-gray-800">
|
||||
Create Decision Record
|
||||
</button>
|
||||
</form>
|
||||
|]
|
||||
|
||||
statusClass :: Text -> Text
|
||||
statusClass "open" = "bg-blue-100 text-blue-700"
|
||||
statusClass "in_review" = "bg-yellow-100 text-yellow-800"
|
||||
|
||||
Reference in New Issue
Block a user