packages feed

gachi-sync-0.1.0.0: src/Gachi/Sync/CI.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Module: Gachi.Sync.CI
-- Description: Forge-tiered CI matrix synthesis and asymmetric runner resource allocation
--
-- This module implements Milestone 3.3 of the Gachi strategic roadmap (~/src/PLAN.md §1.A).
-- It provides pure declarative models and deterministic generators for multi-forge
-- continuous integration (CI) workflows, partitioning physical verification gates
-- across asymmetric runner capabilities.
--
-- = Architectural Context & Invariants
--
-- In accordance with Note [Ecosystem Standardization Invariants]
-- [ref:note-ecosystem-standardization], Note [Cross-Repository Standardization Matrix and Audit Semantics]
-- [ref:note-standardization-matrix], and Note [Forge-Tiered CI Resource Allocation and Static Gate Partitioning]
-- [ref:note-forge-tiered-ci], ecosystem repositories replicate across heterogeneous
-- hosting providers (commercial forges like GitHub, community forges like GitLab/Framagit/Disroot,
-- and sovereign self-hosted instances like Forgejo/Woodpecker/Radicle).
--
-- == Note [Forge-Tiered CI Resource Allocation and Static Gate Partitioning]
-- [tag:note-forge-tiered-ci]
--
-- Modern Haskell toolchains impose heavy compute and memory demands. Compiling
-- an interconnected multi-package workspace under GHC 9.14 with Template Haskell,
-- unboxed primitive arrays, and advanced type families requires multi-gigabyte RAM
-- footprints and significant CPU time.
--
-- Commercial hosting providers (e.g., GitHub Actions) provide high-capacity
-- container runners equipped with 4+ virtual CPUs, 16 GB RAM, generous monthly
-- compute allowances, and global artifact caching services (@actions/cache@).
-- Conversely, community-run and self-hosted forges (e.g., Framagit, Disroot,
-- Codeberg, sourcehut) operate on modest, shared virtual servers where running
-- a full GHC compilation pipeline on every push triggers runner timeouts, CPU
-- throttling, and out-of-memory kernel kills (OOM 137).
--
-- To achieve universal verification without depleting community resources or
-- failing due to runner memory constraints, Gachi establishes an __asymmetric
-- forge-tiered CI matrix__:
--
-- 1.  __High-Resource Commercial Runners ('TierHighResource'):__
--     Allocated to GitHub Actions (@.github/workflows/ci.yml@) and dedicated
--     enterprise Forgejo instances. Executes the complete, heavyweight physical
--     verification suite:
--     * Installs system runtime dependencies (bubblewrap, sqlite3, z3).
--     * Configures AppArmor namespace profiles for bubblewrap container isolation.
--     * Deploys pinned GHC (9.14.1) and Cabal (3.18.1.0) toolchains.
--     * Restores cryptographic Cabal store caches (@~/.cabal/store@, @dist-newstyle@).
--     * Compiles all workspace packages with @--enable-tests@.
--     * Executes HLint static analysis under the zero-hint policy.
--     * Executes all unit, property, and integration test suites.
--     * Verifies comment density thresholds via @./bin/<project>-cloc --check@.
--     * Executes the turnkey five-gate script: @./bin/<project>-verify@.
--
-- 2.  __Resource-Constrained Community Runners ('TierLightweightStatic'):__
--     Allocated to community GitLab instances (Framagit, Disroot: @.gitlab-ci.yml@),
--     Woodpecker CI (@.woodpecker.yml@), and Radicle local verification.
--     Executes fast, deterministic static analysis gates inside a minimal
--     container (@debian:bookworm-slim@) without GHC compilation:
--     * Installs lightweight utilities (curl, git, python3) and Tagref binary.
--     * Verifies Tagref cross-reference integrity (0 dangling references).
--     * Verifies Markdown link integrity and anchor slug resolution (0 broken links).
--     * Verifies in-code comment density thresholds (@bin/<project>-cloc --check@).
--     * Finishes execution in < 15 seconds with < 100 MB memory footprint.
--
-- This division guarantees that community infrastructure remains protected
-- from resource starvation while ensuring every repository maintains zero broken
-- links, zero dangling reference tags, and zero documentation drift on every commit.
--
-- == Economic & Operational Invariants
--
-- The forge-tiered CI matrix adheres to four non-negotiable operational rules:
--
-- * __Purity & Determinism:__ CI configuration files are synthesized purely from
--   the project specification ('Project') and CI specification ('CISpec').
--   No ambient environment variables, timestamps, or network requests are consulted.
--
-- * __Zero Dangling Pointers:__ Every script reference within synthesized CI files
--   (such as @./bin/<project>-verify@ or @./bin/<project>-tagref@) resolves
--   to an executable script generated by the Gachi scaffolding engine.
--
-- * __Cache Key Hermeticity:__ Cache keys on high-resource runners incorporate
--   cryptographic hashes of @cabal.project@ and all @**/*.cabal@ package files,
--   ensuring caches invalidate immediately upon dependency or compiler changes.
--
-- * __Sandbox Isolation:__ High-resource runners execute sandboxed test harnesses
--   under Bubblewrap (@bwrap@). To prevent Ubuntu 24.04 AppArmor user namespace
--   restrictions from aborting builds, synthesized workflows explicitly configure
--   unprivileged user namespace allowances and AppArmor profiles.
--
-- = Worked Example: High-Resource Workflow Generation
--
-- >>> import Gachi.Schema.Project (defaultProject, Author(..))
-- >>> import Gachi.Sync.CI
-- >>> let auth = Author "Nadia Yvette Chambers" "nadia.yvette.chambers@ik.me"
-- >>> let proj = defaultProject "gongju" auth
-- >>> let githubWf = generateGitHubWorkflow defaultGitHubSpec proj
-- >>> "cabal build all --enable-tests" `Data.Text.isInfixOf` githubWf
-- True
-- >>> "ubuntu-latest" `Data.Text.isInfixOf` githubWf
-- True
--
-- = Worked Example: Lightweight Community Workflow Generation
--
-- >>> let gitlabWf = generateGitLabWorkflow defaultGitLabSpec proj
-- >>> "debian:bookworm-slim" `Data.Text.isInfixOf` gitlabWf
-- True
-- >>> "./bin/gongju-tagref" `Data.Text.isInfixOf` gitlabWf
-- True
-- >>> "cabal build" `Data.Text.isInfixOf` gitlabWf
-- False
module Gachi.Sync.CI
  ( -- * Forge and CI Tier Data Models
    ForgeType(..)
  , CITier(..)
  , CISpec(..)

    -- * Pre-Configured Default Specifications
  , defaultGitHubSpec
  , defaultGitLabSpec
  , defaultWoodpeckerSpec
  , defaultForgejoSpec
  , defaultRadicleSpec
  , canonicalForgeSpecs
  , forgeDefaultSpec

    -- * Predicates & Classification Helpers
  , forgeName
  , tierName
  , isHighResource
  , isLightweight
  , classifyForge
  , classifyTier

    -- * Deterministic Workflow Generators
  , generateWorkflow
  , generateGitHubWorkflow
  , generateGitLabWorkflow
  , generateWoodpeckerWorkflow
  , generateForgejoWorkflow
  , generateRadicleWorkflow

    -- * Matrix Synthesis Combinators
  , forgeAssetForSpec
  , synthesizeCIAssets
  , canonicalCIAssets
  ) where

import Data.List (isPrefixOf)
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)

import Gachi.Schema.Project (Project, projectName)
import Gachi.Sync.Types
  ( AssetCategory(..)
  , StandardAsset(..)
  )

--------------------------------------------------------------------------------
-- 1. Forge and CI Tier Data Models
--------------------------------------------------------------------------------

-- | Target hosting forge or CI engine platform.
--
-- Distinguishes commercial clouds with abundant runner quotas from shared
-- community instances and sovereign peer-to-peer repositories.
--
-- Architectural Roles:
-- * 'ForgeGitHub': Commercial cloud runners with 4+ vCPUs, 16 GB RAM, caching.
-- * 'ForgeGitLab': Community shared runners with strict memory and CPU limits.
-- * 'ForgeWoodpecker': Lightweight container pipelines for Forgejo / Codeberg.
-- * 'ForgeForgejo': Self-hosted Git server workflows using Docker runners.
-- * 'ForgeRadicle': Sovereign peer-to-peer identity verification prior to sync.
data ForgeType
  = ForgeGitHub
    -- ^ GitHub Actions (@.github/workflows/ci.yml@): Commercial high-capacity runners.
  | ForgeGitLab
    -- ^ GitLab CI (@.gitlab-ci.yml@): Community runners (Framagit, Disroot, gitlab.com).
  | ForgeWoodpecker
    -- ^ Woodpecker CI (@.woodpecker.yml@): Lightweight container pipelines for Forgejo/Gitea.
  | ForgeForgejo
    -- ^ Forgejo / Gitea Actions (@.forgejo/workflows/ci.yml@): Self-hosted Git server workflows.
  | ForgeRadicle
    -- ^ Radicle P2P (@.radicle/ci.yaml@): Local verification broker for sovereign peer nodes.
  deriving stock (Eq, Show, Ord, Enum, Bounded, Generic)

-- | Resource capability tier assigned to a CI workflow pipeline.
--
-- Governs whether a forge executes the full heavyweight GHC build suite or
-- restricts execution to lightweight static verification gates.
--
-- The allocation strategy guarantees that high-resource runners handle compiler
-- overhead while community runners remain responsive and protected from OOM errors.
data CITier
  = TierHighResource
    -- ^ Heavyweight execution: Full GHC compilation, test suites, Cabal store caching,
    -- AppArmor namespace configuration, and complete physical verification script execution.
  | TierLightweightStatic
    -- ^ Lightweight execution: Fast static gates (Tagref, linkcheck, CLOC audit)
    -- inside a slim container without GHC compiler toolchains (< 15 seconds runtime).
  deriving stock (Eq, Show, Ord, Enum, Bounded, Generic)

-- | Declarative specification governing continuous integration generation.
--
-- Encapsulates the target platform, resource tier, filesystem workflow destination,
-- toolchain versions, container images, and test flags.
--
-- All fields are pure and deterministic, permitting total reproducibility.
data CISpec = CISpec
  { ciForge              :: !ForgeType
    -- ^ Target hosting forge or runner platform.
  , ciTier               :: !CITier
    -- ^ Resource tier governing execution depth (high-resource vs lightweight).
  , ciWorkflowPath       :: !FilePath
    -- ^ Relative workflow path in repository root (e.g. @".github/workflows/ci.yml"@).
  , ciGhcVersion         :: !Text
    -- ^ Target GHC compiler version string (e.g. @"9.14.1"@).
  , ciCabalVersion       :: !Text
    -- ^ Target Cabal specification version string (e.g. @"3.18.1.0"@).
  , ciSystemDependencies :: ![Text]
    -- ^ System apt packages required by high-resource builds (e.g. @["bubblewrap", "sqlite3", "z3"]@).
  , ciContainerImage     :: !Text
    -- ^ Minimal container image for lightweight static runners (e.g. @"debian:bookworm-slim"@).
  , ciEnableTests        :: !Bool
    -- ^ Whether to configure @--enable-tests@ during Cabal compilation.
  , ciEnableDocCheck     :: !Bool
    -- ^ Whether to perform comment density and documentation verification.
  } deriving stock (Eq, Show, Generic)

--------------------------------------------------------------------------------
-- 2. Pre-Configured Default Specifications
--------------------------------------------------------------------------------

-- | Canonical high-resource GitHub Actions specification.
--
-- Configured for full physical verification on Ubuntu latest runners with
-- GHC 9.14.1, Cabal 3.18.1.0, system dependencies, and Cabal store caching.
--
-- Key Properties:
-- * Platform: GitHub Actions (@.github/workflows/ci.yml@).
-- * Resource Tier: 'TierHighResource'.
-- * Compiler: GHC 9.14.1.
-- * Build Tool: Cabal 3.18.1.0.
-- * System Packages: @bubblewrap@, @sqlite3@, @z3@.
-- * Tests Enabled: 'True'.
-- * Doc Check Enabled: 'True'.
defaultGitHubSpec :: CISpec
defaultGitHubSpec = CISpec
  { ciForge              = ForgeGitHub
  , ciTier               = TierHighResource
  , ciWorkflowPath       = ".github/workflows/ci.yml"
  , ciGhcVersion         = "9.14.1"
  , ciCabalVersion       = "3.18.1.0"
  , ciSystemDependencies = ["bubblewrap", "sqlite3", "z3"]
  , ciContainerImage     = "ubuntu-latest"
  , ciEnableTests        = True
  , ciEnableDocCheck     = True
  }

-- | Canonical lightweight GitLab CI specification for community runners.
--
-- Configured for Framagit and Disroot using @debian:bookworm-slim@.
-- Runs Tagref, linkcheck, and CLOC audit in < 15 seconds without GHC.
--
-- Key Properties:
-- * Platform: GitLab CI (@.gitlab-ci.yml@).
-- * Resource Tier: 'TierLightweightStatic'.
-- * Image: @debian:bookworm-slim@.
-- * System Packages: @curl@, @git@, @python3@.
-- * Tests Enabled: 'False' (avoids GHC compilation).
-- * Doc Check Enabled: 'True' (executes @bin/*-cloc --check@).
defaultGitLabSpec :: CISpec
defaultGitLabSpec = CISpec
  { ciForge              = ForgeGitLab
  , ciTier               = TierLightweightStatic
  , ciWorkflowPath       = ".gitlab-ci.yml"
  , ciGhcVersion         = "9.14.1"
  , ciCabalVersion       = "3.18.1.0"
  , ciSystemDependencies = ["curl", "git", "python3"]
  , ciContainerImage     = "debian:bookworm-slim"
  , ciEnableTests        = False
  , ciEnableDocCheck     = True
  }

-- | Canonical lightweight Woodpecker CI specification.
--
-- Used on self-hosted Woodpecker servers connected to Forgejo or Codeberg.
--
-- Key Properties:
-- * Platform: Woodpecker CI (@.woodpecker.yml@).
-- * Resource Tier: 'TierLightweightStatic'.
-- * Image: @debian:bookworm-slim@.
-- * System Packages: @curl@, @git@, @python3@.
defaultWoodpeckerSpec :: CISpec
defaultWoodpeckerSpec = CISpec
  { ciForge              = ForgeWoodpecker
  , ciTier               = TierLightweightStatic
  , ciWorkflowPath       = ".woodpecker.yml"
  , ciGhcVersion         = "9.14.1"
  , ciCabalVersion       = "3.18.1.0"
  , ciSystemDependencies = ["curl", "git", "python3"]
  , ciContainerImage     = "debian:bookworm-slim"
  , ciEnableTests        = False
  , ciEnableDocCheck     = True
  }

-- | Canonical Forgejo Actions specification.
--
-- High-resource containerized pipeline for sovereign Forgejo instances.
--
-- Key Properties:
-- * Platform: Forgejo Actions (@.forgejo/workflows/ci.yml@).
-- * Resource Tier: 'TierHighResource'.
-- * Compiler: GHC 9.14.1.
-- * Image: @node:20-bookworm@.
defaultForgejoSpec :: CISpec
defaultForgejoSpec = CISpec
  { ciForge              = ForgeForgejo
  , ciTier               = TierHighResource
  , ciWorkflowPath       = ".forgejo/workflows/ci.yml"
  , ciGhcVersion         = "9.14.1"
  , ciCabalVersion       = "3.18.1.0"
  , ciSystemDependencies = ["bubblewrap", "sqlite3", "z3"]
  , ciContainerImage     = "node:20-bookworm"
  , ciEnableTests        = True
  , ciEnableDocCheck     = True
  }

-- | Canonical Radicle P2P local verification broker specification.
--
-- Lightweight static check script invoked by Radicle node post-receive hooks.
--
-- Key Properties:
-- * Platform: Radicle P2P (@.radicle/ci.yaml@).
-- * Resource Tier: 'TierLightweightStatic'.
-- * Verification Scope: Tagref, Linkcheck, Comment Density.
defaultRadicleSpec :: CISpec
defaultRadicleSpec = CISpec
  { ciForge              = ForgeRadicle
  , ciTier               = TierLightweightStatic
  , ciWorkflowPath       = ".radicle/ci.yaml"
  , ciGhcVersion         = "9.14.1"
  , ciCabalVersion       = "3.18.1.0"
  , ciSystemDependencies = ["curl", "git", "python3"]
  , ciContainerImage     = "debian:bookworm-slim"
  , ciEnableTests        = False
  , ciEnableDocCheck     = True
  }

-- | List of authoritative CI specifications registered across standard ecosystem forges.
--
-- Represents the default triad:
-- 1. GitHub Actions (Commercial High-Resource)
-- 2. GitLab CI (Community Lightweight Static)
-- 3. Woodpecker CI (Self-Hosted Lightweight Static)
canonicalForgeSpecs :: [CISpec]
canonicalForgeSpecs =
  [ defaultGitHubSpec
  , defaultGitLabSpec
  , defaultWoodpeckerSpec
  ]

-- | Returns the canonical default 'CISpec' for a given 'ForgeType' and 'CITier'.
--
-- Enables parameterized lookup when generating tailored workflows for custom targets.
forgeDefaultSpec :: ForgeType -> CITier -> CISpec
-- GitHub Actions: High resource (default) or lightweight override
forgeDefaultSpec ForgeGitHub TierHighResource      = defaultGitHubSpec
forgeDefaultSpec ForgeGitHub TierLightweightStatic =
  defaultGitHubSpec { ciTier = TierLightweightStatic, ciEnableTests = False }
-- GitLab CI: Lightweight static (default) or high resource container override
forgeDefaultSpec ForgeGitLab TierLightweightStatic = defaultGitLabSpec
forgeDefaultSpec ForgeGitLab TierHighResource      =
  defaultGitLabSpec { ciTier = TierHighResource, ciEnableTests = True, ciContainerImage = "haskell:9.14" }
-- Woodpecker CI: Lightweight static (default) or high resource container override
forgeDefaultSpec ForgeWoodpecker TierLightweightStatic = defaultWoodpeckerSpec
forgeDefaultSpec ForgeWoodpecker TierHighResource      =
  defaultWoodpeckerSpec { ciTier = TierHighResource, ciEnableTests = True, ciContainerImage = "haskell:9.14" }
-- Forgejo Actions: High resource (default) or lightweight static override
forgeDefaultSpec ForgeForgejo TierHighResource      = defaultForgejoSpec
forgeDefaultSpec ForgeForgejo TierLightweightStatic =
  defaultForgejoSpec { ciTier = TierLightweightStatic, ciEnableTests = False }
-- Radicle P2P: Sovereign static verification broker
forgeDefaultSpec ForgeRadicle _                     = defaultRadicleSpec

--------------------------------------------------------------------------------
-- 3. Predicates & Classification Helpers
--------------------------------------------------------------------------------

-- | Human-readable name of a 'ForgeType'.
--
-- Used in diagnostic banners and synchronization audit reports.
forgeName :: ForgeType -> Text
forgeName ForgeGitHub     = "GitHub Actions"
forgeName ForgeGitLab     = "GitLab CI (Framagit, Disroot)"
forgeName ForgeWoodpecker = "Woodpecker CI"
forgeName ForgeForgejo    = "Forgejo Actions"
forgeName ForgeRadicle    = "Radicle P2P CI"

-- | Human-readable description of a 'CITier'.
--
-- Highlights whether a pipeline executes heavyweight GHC verification or fast static gates.
tierName :: CITier -> Text
tierName TierHighResource      = "High-Resource (Full Physical Verification)"
tierName TierLightweightStatic = "Lightweight (Static Gates Only)"

-- | Predicate checking if a specification belongs to the high-resource tier.
isHighResource :: CISpec -> Bool
isHighResource spec = ciTier spec == TierHighResource

-- | Predicate checking if a specification belongs to the lightweight tier.
isLightweight :: CISpec -> Bool
isLightweight spec = ciTier spec == TierLightweightStatic

-- | Infers the 'ForgeType' from a workflow file path.
--
-- Inspects standard file names and directories:
-- * @.github/workflows/*@ -> 'ForgeGitHub'
-- * @.gitlab-ci.yml@ -> 'ForgeGitLab'
-- * @.woodpecker.yml@ or @.woodpecker/*@ -> 'ForgeWoodpecker'
-- * @.forgejo/workflows/*@ -> 'ForgeForgejo'
-- * @.radicle/*@ -> 'ForgeRadicle'
--
-- Returns 'Nothing' if the path does not match recognized CI conventions.
classifyForge :: FilePath -> Maybe ForgeType
classifyForge path
  -- GitHub Actions workflow directory
  | ".github/workflows/" `isPrefixOf` path = Just ForgeGitHub
  -- GitLab CI configuration file in root
  | path == ".gitlab-ci.yml"               = Just ForgeGitLab
  -- Woodpecker CI single file configuration
  | path == ".woodpecker.yml"              = Just ForgeWoodpecker
  -- Woodpecker CI pipeline directory
  | ".woodpecker/" `isPrefixOf` path       = Just ForgeWoodpecker
  -- Forgejo Actions workflow directory
  | ".forgejo/" `isPrefixOf` path          = Just ForgeForgejo
  -- Radicle P2P CI verification directory
  | ".radicle/" `isPrefixOf` path          = Just ForgeRadicle
  -- Unrecognized path
  | otherwise                              = Nothing

-- | Infers the expected 'CITier' from a standard workflow path.
--
-- Recognizes high-resource vs lightweight tiers based on repository path conventions.
classifyTier :: FilePath -> Maybe CITier
classifyTier path
  -- GitHub Actions executes full verification suite
  | ".github/" `isPrefixOf` path  = Just TierHighResource
  -- GitLab CI community runners execute static gates only
  | path == ".gitlab-ci.yml"      = Just TierLightweightStatic
  -- Woodpecker CI executes lightweight container steps
  | path == ".woodpecker.yml"     = Just TierLightweightStatic
  -- Forgejo enterprise runners execute full suite
  | ".forgejo/" `isPrefixOf` path = Just TierHighResource
  -- Radicle local node broker executes static gates
  | ".radicle/" `isPrefixOf` path = Just TierLightweightStatic
  -- Unrecognized path
  | otherwise                     = Nothing

--------------------------------------------------------------------------------
-- 4. Deterministic Workflow Generators
--------------------------------------------------------------------------------

-- | Pure dispatcher synthesizing the appropriate CI configuration text for a 'CISpec'.
--
-- Evaluates the target forge platform and invokes the corresponding deterministic generator.
generateWorkflow :: CISpec -> Project -> Text
generateWorkflow spec proj = case ciForge spec of
  ForgeGitHub     -> generateGitHubWorkflow spec proj
  ForgeGitLab     -> generateGitLabWorkflow spec proj
  ForgeWoodpecker -> generateWoodpeckerWorkflow spec proj
  ForgeForgejo    -> generateForgejoWorkflow spec proj
  ForgeRadicle    -> generateRadicleWorkflow spec proj

-- | Generates GitHub Actions workflow configuration (@.github/workflows/ci.yml@).
--
-- Implements the high-resource physical verification pipeline:
--
-- 1.  __Checkout Step:__
--     Uses @actions/checkout@v4@ to clone the repository with full git history.
--
-- 2.  __Tagref Installation & Check:__
--     Downloads the authoritative Tagref binary and executes @./bin/<project>-tagref@
--     to verify that zero dangling reference tags or duplicate tag definitions exist.
--
-- 3.  __Markdown Link & Anchor Gate:__
--     Executes @./bin/<project>-linkcheck@ to verify 100% of intra-corpus relative
--     links and section heading anchors across all markdown documentation files.
--
-- 4.  __System Dependencies & AppArmor Setup:__
--     Installs system runtime libraries (@bubblewrap@, @sqlite3@, @z3@).
--     Configures unconfined AppArmor profile for Bubblewrap (@bwrap@) to enable
--     unprivileged user namespace creation on Ubuntu 24.04 runner environments.
--
-- 5.  __Git Submodule Protocol Rewriting:__
--     Configures Git to rewrite @git@github.com:@ SSH URLs to HTTPS for clean submodule pulls.
--
-- 6.  __Haskell Toolchain Provisioning:__
--     Invokes @haskell-actions/setup@v2@ with pinned GHC (9.14.1) and Cabal (3.18.1.0).
--
-- 7.  __HLint Static Analysis Installation:__
--     Installs the recommended HLint binary via GHCup and runs under zero-hint cleanliness.
--
-- 8.  __Cryptographic Artifact Caching:__
--     Invokes @actions/cache@v4@ caching @~/.cabal/store@ and @dist-newstyle@ keyed on
--     the combined cryptographic hash of @cabal.project@ and all @**/*.cabal@ manifests.
--
-- 9.  __Compilation & Test Build:__
--     Executes @cabal build all --enable-tests@ compiling all workspace packages and test suites.
--
-- 10. __Comment Density Audit:__
--     Executes @./bin/<project>-cloc --check@ enforcing documentation thresholds.
--
-- 11. __Turnkey Verification Harness:__
--     Executes @./bin/<project>-verify@ ensuring all 5 physical gates pass in unison.
generateGitHubWorkflow :: CISpec -> Project -> Text
generateGitHubWorkflow spec proj =
  let pName = projectName proj
      ghcVer = ciGhcVersion spec
      cabalVer = ciCabalVersion spec
      deps = T.unwords (ciSystemDependencies spec)
  in T.unlines
    [ -- SPDX License identifier declaring legal terms
      "# SPDX-License-Identifier: EUPL-1.2"
      -- Canonical repository metadata comment
    , "# Canonical GitHub Actions CI Workflow for " <> pName
      -- Cross-reference pointer grounding the Note in the CI asset
    , "# Note [Forge-Tiered CI Resource Allocation and Static Gate Partitioning] [ref:note-forge-tiered-ci]"
    , ""
      -- Top-level workflow descriptive name
    , "name: CI Verification Harness"
    , ""
      -- Trigger definitions: executed on push and pull-request events targeting master
    , "on:"
    , "  push:"
    , "    branches: [ master ]"
    , "  pull_request:"
    , "    branches: [ master ]"
    , ""
      -- Job definition block
    , "jobs:"
    , "  verify:"
    , "    name: Tagref & Verification Gates"
      -- Host runner operating system targeting Ubuntu LTS
    , "    runs-on: ubuntu-latest"
    , "    steps:"
      -- Step 1: Check out git working tree
    , "      - name: Check out repository"
    , "        uses: actions/checkout@v4"
    , ""
      -- Step 2: Download and install Tagref cross-reference binary
    , "      - name: Install Tagref"
    , "        run: |"
    , "          mkdir -p \"${HOME}/.local/bin\""
    , "          PREFIX=\"${HOME}/.local/bin\" curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | sh"
    , "          echo \"${HOME}/.local/bin\" >> $GITHUB_PATH"
    , ""
      -- Step 3: Run static Tagref verification gate
    , "      - name: Verify Tagref Cross-References"
    , "        run: ./bin/" <> pName <> "-tagref"
    , ""
      -- Step 4: Run intra-corpus Markdown hyperlink and heading anchor verification
    , "      - name: Verify Markdown Links & Anchors"
    , "        run: ./bin/" <> pName <> "-linkcheck"
    , ""
      -- Step 5: Install runtime system dependencies and configure AppArmor sandbox profiles
    , "      - name: Install System Dependencies"
    , "        run: |"
    , "          sudo apt-get update"
    , "          sudo apt-get install -y " <> deps
      -- Disable Ubuntu 24.04 kernel restriction on unprivileged user namespaces
    , "          sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 2>/dev/null || true"
      -- Create unconfined AppArmor profile for Bubblewrap if profile directory exists
    , "          if [ -d /etc/apparmor.d ]; then"
    , "            printf 'abi <abi/4.0>,\\ninclude <tunables/global>\\nprofile bwrap /usr/bin/bwrap flags=(unconfined) {\\n    userns,\\n    include if exists <local/bwrap>\\n}\\n' | sudo tee /etc/apparmor.d/bwrap >/dev/null || true"
    , "            sudo apparmor_parser -r /etc/apparmor.d/bwrap 2>/dev/null || true"
    , "          fi"
    , ""
      -- Step 6: Rewrite SSH Git submodule URLs to HTTPS to avoid unauthenticated SSH failures
    , "      - name: Configure Git for HTTPS submodules"
    , "        run: git config --global url.\"https://github.com/\".insteadOf \"git@github.com:\""
    , ""
      -- Step 7: Configure pinned GHC and Cabal toolchain via official Haskell action
    , "      - name: Set up Haskell Toolchain"
    , "        uses: haskell-actions/setup@v2"
    , "        with:"
    , "          ghc-version: '" <> ghcVer <> "'"
    , "          cabal-version: '" <> cabalVer <> "'"
    , ""
      -- Step 8: Install HLint binary
    , "      - name: Install HLint"
    , "        run: |"
    , "          mkdir -p \"${HOME}/.local/bin\""
    , "          if ! command -v hlint >/dev/null 2>&1; then"
    , "            curl -sSL https://github.com/ndmitchell/hlint/releases/download/v3.10/hlint-3.10-x86_64-linux.tar.gz | tar -xz -C \"${HOME}/.local\""
    , "            ln -sf \"${HOME}/.local/hlint-3.10/hlint\" \"${HOME}/.local/bin/hlint\""
    , "          fi"
    , "          echo \"${HOME}/.local/bin\" >> $GITHUB_PATH"
    , ""
      -- Step 9: Restore and save Cabal store caches keyed by project manifests
    , "      - name: Cache Cabal artifacts"
    , "        uses: actions/cache@v4"
    , "        with:"
    , "          path: |"
    , "            ~/.cabal/store"
    , "            dist-newstyle"
    , "          key: ${{ runner.os }}-cabal-${{ hashFiles('cabal.project', '**/*.cabal') }}"
    , "          restore-keys: |"
    , "            ${{ runner.os }}-cabal-"
    , ""
      -- Step 10: Compile all workspace packages and test harnesses
    , "      - name: Build All Packages & Dependencies"
    , "        run: cabal build all" <> (if ciEnableTests spec then " --enable-tests" else "")
    , ""
      -- Step 11: Execute documentation comment density audit
    , "      - name: Verify Comment Density Audit"
    , "        run: ./bin/" <> pName <> "-cloc --check"
    , ""
      -- Step 12: Run complete 5-gate physical verification harness
    , "      - name: Run Full Physical Verification Gates"
    , "        run: ./bin/" <> pName <> "-verify"
    ]

-- | Generates GitLab CI configuration (@.gitlab-ci.yml@).
--
-- Tailored to protect resource-constrained community runners (Framagit, Disroot).
-- Runs fast static analysis gates inside @debian:bookworm-slim@ without GHC overhead.
--
-- The pipeline executes in under 15 seconds, verifying Tagref cross-references,
-- Markdown links, and CLOC comment density while consuming negligible compute quota.
generateGitLabWorkflow :: CISpec -> Project -> Text
generateGitLabWorkflow spec proj =
  let pName = projectName proj
      cImage = ciContainerImage spec
  in if ciTier spec == TierLightweightStatic
    then T.unlines
      [ -- SPDX license header
        "# SPDX-License-Identifier: EUPL-1.2"
        -- Target forge and provider documentation
      , "# GitLab CI configuration for " <> pName <> " (Framagit, Disroot)"
        -- Canonical Note reference
      , "# Note [Forge-Tiered CI Resource Allocation and Static Gate Partitioning] [ref:note-forge-tiered-ci]"
      , ""
        -- Verification stage list
      , "stages:"
      , "  - verify"
      , ""
        -- Static verification job definition
      , "tagref-check:"
      , "  stage: verify"
        -- Lean base container image
      , "  image: " <> cImage
        -- Pre-flight script installing curl, git, python3, and Tagref
      , "  before_script:"
      , "    - apt-get update && apt-get install -y curl git python3"
      , "    - mkdir -p /usr/local/bin"
      , "    - PREFIX=/usr/local/bin curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | sh"
        -- Static gate commands: Tagref, Linkcheck, and CLOC density check
      , "  script:"
      , "    - ./bin/" <> pName <> "-tagref"
      , "    - ./bin/" <> pName <> "-linkcheck"
      , "    - ./bin/" <> pName <> "-cloc --check"
      ]
    else T.unlines
      [ -- SPDX license header
        "# SPDX-License-Identifier: EUPL-1.2"
        -- Target forge documentation
      , "# High-Resource GitLab CI configuration for " <> pName
        -- Canonical Note reference
      , "# Note [Forge-Tiered CI Resource Allocation and Static Gate Partitioning] [ref:note-forge-tiered-ci]"
      , ""
        -- Stage definition
      , "stages:"
      , "  - verify"
      , ""
        -- Full verification job definition
      , "full-verify:"
      , "  stage: verify"
        -- Haskell container image
      , "  image: " <> cImage
        -- Pre-flight script installing system libraries and Tagref
      , "  before_script:"
      , "    - apt-get update && apt-get install -y curl git python3 bubblewrap sqlite3 z3"
      , "    - mkdir -p /usr/local/bin"
      , "    - PREFIX=/usr/local/bin curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | sh"
        -- Full build and turnkey verification commands
      , "  script:"
      , "    - cabal build all --enable-tests"
      , "    - ./bin/" <> pName <> "-verify"
      ]

-- | Generates Woodpecker CI pipeline configuration (@.woodpecker.yml@).
--
-- Used on lightweight, containerized Woodpecker agents attached to Forgejo.
--
-- Executes the three lightweight static verification gates inside @debian:bookworm-slim@.
generateWoodpeckerWorkflow :: CISpec -> Project -> Text
generateWoodpeckerWorkflow spec proj =
  let pName = projectName proj
      cImage = ciContainerImage spec
  in T.unlines
    [ -- SPDX license header
      "# SPDX-License-Identifier: EUPL-1.2"
      -- Target forge documentation
    , "# Woodpecker CI pipeline for " <> pName
      -- Canonical Note reference
    , "# Note [Forge-Tiered CI Resource Allocation and Static Gate Partitioning] [ref:note-forge-tiered-ci]"
    , ""
      -- Woodpecker pipeline root stanza
    , "pipeline:"
    , "  static-verify:"
        -- Container image specification
      , "    image: " <> cImage
        -- Pipeline command sequence
      , "    commands:"
      , "      - apt-get update && apt-get install -y curl git python3"
      , "      - mkdir -p /usr/local/bin"
      , "      - PREFIX=/usr/local/bin curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | sh"
      , "      - ./bin/" <> pName <> "-tagref"
      , "      - ./bin/" <> pName <> "-linkcheck"
      , "      - ./bin/" <> pName <> "-cloc --check"
    ]

-- | Generates Forgejo Actions workflow configuration (@.forgejo/workflows/ci.yml@).
--
-- Compatible with GitHub Actions workflow syntax for self-hosted runners.
--
-- Executes containerized verification pipelines in sovereign forge deployments.
generateForgejoWorkflow :: CISpec -> Project -> Text
generateForgejoWorkflow =
  -- Reuses GitHub Actions syntax engine with Forgejo specification
  generateGitHubWorkflow

-- | Generates Radicle P2P local verification broker configuration (@.radicle/ci.yaml@).
--
-- Executed locally on peer developer nodes prior to peer-to-peer patch distribution.
--
-- Ensures patches meet strict Tagref, linkcheck, and comment density standards before dissemination.
generateRadicleWorkflow :: CISpec -> Project -> Text
generateRadicleWorkflow _spec proj =
  let pName = projectName proj
  in T.unlines
    [ -- SPDX license header
      "# SPDX-License-Identifier: EUPL-1.2"
      -- Radicle verification broker documentation
    , "# Radicle P2P CI Verification Configuration for " <> pName
      -- Canonical Note reference
    , "# Note [Forge-Tiered CI Resource Allocation and Static Gate Partitioning] [ref:note-forge-tiered-ci]"
    , ""
      -- Radicle pipeline schema version
    , "version: 1"
    , "pipelines:"
    , "  - name: static-gates"
        -- Event triggers: runs on branch pushes and patch publications
      , "    trigger: [ push, patch ]"
      , "    steps:"
        -- Step 1: Run Tagref cross-reference verification
      , "      - name: Tagref"
      , "        command: ./bin/" <> pName <> "-tagref"
        -- Step 2: Run Markdown link and anchor verification
      , "      - name: Linkcheck"
      , "        command: ./bin/" <> pName <> "-linkcheck"
        -- Step 3: Run comment density audit
      , "      - name: CLOC"
      , "        command: ./bin/" <> pName <> "-cloc --check"
    ]

--------------------------------------------------------------------------------
-- 5. Matrix Synthesis Combinators
--------------------------------------------------------------------------------

-- | Converts a 'CISpec' into a canonical 'StandardAsset' in 'CategoryCIWorkflow'.
--
-- Sets requirement flags:
-- * GitHub (@.github/workflows/ci.yml@) and GitLab (@.gitlab-ci.yml@) are mandatory ('assetRequired' = True).
-- * Ancillary forges (Woodpecker, Forgejo, Radicle) are optional ('assetRequired' = False).
forgeAssetForSpec :: CISpec -> StandardAsset
forgeAssetForSpec spec =
  let path = ciWorkflowPath spec
      -- Mandatory assets enforced across every ecosystem repository
      isReq = path `elem` [".github/workflows/ci.yml", ".gitlab-ci.yml"]
      -- Human-readable asset description
      desc = case ciForge spec of
        ForgeGitHub     -> "High-resource GitHub Actions workflow running complete physical gates"
        ForgeGitLab     -> "Lightweight GitLab CI workflow for resource-constrained community forges"
        ForgeWoodpecker -> "Lightweight Woodpecker CI workflow for Forgejo/Gitea runners"
        ForgeForgejo    -> "Self-hosted Forgejo Actions workflow running containerized verification"
        ForgeRadicle    -> "Radicle sovereign peer-to-peer verification pipeline"
  in StandardAsset
    { assetPath        = path
    , assetCategory    = CategoryCIWorkflow
    , assetRequired    = isReq
    , assetExecutable  = False
    , assetDescription = desc
    , assetGenerator   = generateWorkflow spec
    }

-- | Synthesizes a list of 'StandardAsset' records from a collection of 'CISpec' configurations.
--
-- Maps each specification into a standard matrix asset ready for auditing and propagation.
synthesizeCIAssets :: [CISpec] -> [StandardAsset]
synthesizeCIAssets = map forgeAssetForSpec

-- | The canonical list of standard CI workflow assets registered across all ecosystem checkouts.
--
-- Encompasses GitHub Actions, GitLab CI, and Woodpecker CI pipelines.
canonicalCIAssets :: [StandardAsset]
canonicalCIAssets = synthesizeCIAssets canonicalForgeSpecs