diff --git a/profile/seihou-registry-docs.dhall b/profile/seihou-registry-docs.dhall
--- a/profile/seihou-registry-docs.dhall
+++ b/profile/seihou-registry-docs.dhall
@@ -26,6 +26,7 @@
       | Integer
       | NonNegativeInteger
       | Boolean
+      | FiniteDecimal
       >
 
 let FieldCondition = { field : Text, hasValue : List Text }
@@ -51,8 +52,26 @@
       , reference : Optional HandleReferenceRule
       }
 
+let ValueSelector = { field : Text, member : Optional Text }
+
+let PresenceConstraint =
+      { when : Optional FieldCondition
+      , whenAnyPresent : List ValueSelector
+      , requireAll : List ValueSelector
+      , requireAny : List ValueSelector
+      }
+
+let TimestampOrderConstraint = { before : Text, after : Text }
+
+let MembershipConstraint = { source : ValueSelector, target : ValueSelector }
+
+let UniqueValuesConstraint = { selectors : List ValueSelector }
+
 let NestedRules =
-      { required : List NestedFieldRule
+      { allowUnknownFields : Bool
+      , presenceConstraints : List PresenceConstraint
+      , timestampOrderConstraints : List TimestampOrderConstraint
+      , required : List NestedFieldRule
       , recommended : List NestedFieldRule
       , optional : List NestedFieldRule
       }
@@ -72,13 +91,18 @@
       }
 
 let FrontmatterRules =
-      { required : List FieldRule
+      { presenceConstraints : List PresenceConstraint
+      , timestampOrderConstraints : List TimestampOrderConstraint
+      , membershipConstraints : List MembershipConstraint
+      , uniqueValuesConstraints : List UniqueValuesConstraint
+      , required : List FieldRule
       , recommended : List FieldRule
       , optional : List FieldRule
       }
 
 let TypeRule =
-      { type : Text
+      { guidance : Optional Text
+      , type : Text
       , description : Optional Text
       , frontmatter : FrontmatterRules
       , pathPattern : Optional Text
@@ -90,11 +114,25 @@
 
 let emptyRules
     : FrontmatterRules
-    = { required = [] : List FieldRule
+    = { presenceConstraints = [] : List PresenceConstraint
+      , timestampOrderConstraints = [] : List TimestampOrderConstraint
+      , membershipConstraints = [] : List MembershipConstraint
+      , uniqueValuesConstraints = [] : List UniqueValuesConstraint
+      , required = [] : List FieldRule
       , recommended = [] : List FieldRule
       , optional = [] : List FieldRule
       }
 
+let emptyNestedRules
+    : NestedRules
+    = { allowUnknownFields = True
+      , presenceConstraints = [] : List PresenceConstraint
+      , timestampOrderConstraints = [] : List TimestampOrderConstraint
+      , required = [] : List NestedFieldRule
+      , recommended = [] : List NestedFieldRule
+      , optional = [] : List NestedFieldRule
+      }
+
 let nestedScalar =
       \(name : Text) ->
       \(description : Text) ->
@@ -141,7 +179,8 @@
       \(name : Text) ->
       \(description : Text) ->
       \(directory : Text) ->
-        { type = name
+        { guidance = None Text
+        , type = name
         , description = Some description
         , frontmatter = emptyRules // { optional = [ versionRule ] }
         , pathPattern = Some "${directory}/*"
@@ -151,9 +190,10 @@
         , idPrefix = None Text
         }
 
-in  { name = "seihou-registry-docs"
+in  { guidance = None Text
+    , name = "seihou-registry-docs"
     , description = Some
-        "Generated documentation for a Seihou registry: one concept per published module, recipe, blueprint and agent prompt, plus one describing the registry itself. Every concept names the registry artifact it was derived from through a `seihou://` resource and records the generator that produced it, so a reader can tell derived documentation from hand-written prose and can find the `.dhall` source it came from."
+        "Generated documentation for a Seihou registry: one concept per published module, recipe, blueprint, agent prompt and judgement, plus one describing the registry itself. Every concept names the registry artifact it was derived from through a `seihou://` resource and records the generator that produced it, so a reader can tell derived documentation from hand-written prose and can find the `.dhall` source it came from."
     , okfVersion = "0.2"
     , frontmatter =
             emptyRules
@@ -175,7 +215,7 @@
                   Cardinality.Any
                   (None FieldFormat)
                   ( Some
-                      (     emptyRules
+                      (     emptyNestedRules
                         //  { required =
                               [     nestedScalar
                                       "by"
@@ -192,16 +232,15 @@
                       )
                   )
               ]
-            , recommended =
+            , recommended = [] : List FieldRule
+            , optional =
               [ field
                   "tags"
-                  "The tags the registry catalog records for this artifact."
+                  "Catalog tags, when nonempty. An empty tag list is a valid registry declaration; generation never invents tags to satisfy the profile."
                   Cardinality.List
                   (None FieldFormat)
                   (None NestedRules)
-              ]
-            , optional =
-              [ field
+              , field
                   "status"
                   "The concept's lifecycle status, per OKF specification section 5.4."
                   Cardinality.Scalar
@@ -216,11 +255,11 @@
     , types =
       [ artifactType
           "SeihouModule"
-          "A deterministic file-generation template: variables, generation steps, shell commands, dependencies, an optional removal procedure and optional migration edges."
+          "A deterministic file-generation template: variables, generation steps, shell commands, dependencies, managed/seed/checkout ownership, an optional removal procedure, judgement references and optional migration edges."
           "modules"
       , artifactType
           "SeihouRecipe"
-          "A named composition of modules with preset variable bindings."
+          "A declared composition of modules with sourced dependencies, preset variable bindings, an optional single base and explicit overrides."
           "recipes"
       , artifactType
           "SeihouBlueprint"
@@ -230,6 +269,10 @@
           "SeihouPrompt"
           "A reusable agent-session template."
           "prompts"
+      , artifactType
+          "SeihouJudgement"
+          "Yes/no checks Seihou asks a model about staged changes or a commit range before a commit, with per-check thresholds and enforcement."
+          "judgements"
       ,     artifactType
               "SeihouRegistry"
               "The registry itself: the repository publishing these artifacts, linking each of them."
diff --git a/seihou-okf-extension.cabal b/seihou-okf-extension.cabal
--- a/seihou-okf-extension.cabal
+++ b/seihou-okf-extension.cabal
@@ -1,11 +1,11 @@
 cabal-version: 3.0
 name: seihou-okf-extension
-version: 0.15.0.0
+version: 0.16.0.0
 synopsis: OKF documentation extension for Seihou registries
 description:
   External Seihou extension executable for generating OKF documentation bundles
-  from Seihou registries. The initial package establishes the extension boundary;
-  later plans add registry loading, rendering, and the real docs command.
+  from Seihou registry declarations. Covers modules, recipes, blueprints, prompts
+  and judgements with strict OKF validation and an embedded standalone profile.
 
 homepage: https://github.com/shinzui/seihou
 bug-reports: https://github.com/shinzui/seihou/issues
@@ -58,9 +58,9 @@
     filepath >=1.4 && <2,
     generic-lens >=2.2 && <3,
     lens >=5.2 && <6,
-    okf-core ^>=0.10.0.0,
+    okf-core ^>=0.13.0.0,
     optparse-applicative >=0.18 && <1,
-    seihou-core ^>=0.15.0.0,
+    seihou-core ^>=0.16.0.0,
     temporary >=1.3 && <2,
     text >=2.0 && <3,
 
@@ -110,15 +110,17 @@
     Seihou.OKF.Extension.DocsSpec
 
   build-depends:
+    aeson >=2.1 && <3,
     base >=4.18 && <5,
     containers >=0.6 && <1,
+    dhall >=1.42 && <2,
     directory >=1.3 && <2,
     filepath >=1.4 && <2,
     generic-lens >=2.2 && <3,
     hspec >=2.11 && <3,
     lens >=5.2 && <6,
-    okf-core ^>=0.10.0.0,
-    seihou-core ^>=0.15.0.0,
+    okf-core ^>=0.13.0.0,
+    seihou-core ^>=0.16.0.0,
     seihou-okf-extension-internal,
     tasty >=1.4 && <2,
     tasty-hspec >=1.2 && <2,
diff --git a/src/Seihou/OKF/Docs/Model.hs b/src/Seihou/OKF/Docs/Model.hs
--- a/src/Seihou/OKF/Docs/Model.hs
+++ b/src/Seihou/OKF/Docs/Model.hs
@@ -3,6 +3,7 @@
     DocArtifact (..),
     DocEntry (..),
     ModuleRef (..),
+    JudgementDocRef (..),
     BaseRecipeRef (..),
     EntailedRef (..),
     DocModel (..),
@@ -13,23 +14,27 @@
 
 import Control.Lens ((%~), (&), (.~), (^.))
 import Data.Generics.Labels ()
+import Data.Map.Strict (Map)
 import Data.Text qualified as T
 import GHC.Generics (Generic)
+import Seihou.Core.Judgement (JudgementDefinition, JudgementRef)
 import Seihou.Core.Migration (BlueprintMigration (..))
 import Seihou.Core.Registry (Registry (..), RegistryEntry (..))
 import Seihou.Core.Types
   ( AgentPrompt,
     Blueprint (..),
-    Dependency,
+    Dependency (..),
     Module (..),
     ModuleLoadError,
     Recipe (..),
     RecipeReference (..),
-    depModuleNames,
+    VarName,
   )
 import Seihou.Dhall.Eval
   ( evalAgentPromptFromFile,
     evalBlueprintFromFile,
+    evalJudgementFromFile,
+    evalJudgementRefsFromFile,
     evalModuleFromFile,
     evalRecipeFromFile,
     evalRegistryFromFile,
@@ -37,7 +42,7 @@
 import System.Directory (doesFileExist)
 import System.FilePath ((</>))
 
--- | The kinds of concept this bundle emits. The first four are registry entry
+-- | The kinds of concept this bundle emits. The first five are registry entry
 -- kinds; 'DocRegistryKind' belongs to the single overview concept describing the
 -- registry itself, and never appears as a 'DocEntry' kind.
 data DocKind
@@ -45,6 +50,7 @@
   | DocRecipeKind
   | DocBlueprintKind
   | DocPromptKind
+  | DocJudgementKind
   | DocRegistryKind
   deriving stock (Eq, Show)
 
@@ -53,6 +59,7 @@
   | DocRecipeArtifact Recipe
   | DocBlueprintArtifact Blueprint
   | DocPromptArtifact AgentPrompt
+  | DocJudgementArtifact JudgementDefinition
   deriving stock (Eq, Show)
 
 data DocEntry = DocEntry
@@ -68,7 +75,8 @@
     -- | Every edge this entry\'s migrations entail, flattened across the
     -- entry\'s migration edges in declaration order. Empty for every kind but
     -- a blueprint, which is the only artifact that can declare entailment.
-    entailedRefs :: ![EntailedRef]
+    entailedRefs :: ![EntailedRef],
+    judgementRefs :: ![JudgementDocRef]
   }
   deriving stock (Eq, Generic, Show)
 
@@ -81,10 +89,19 @@
 
 data ModuleRef = ModuleRef
   { name :: !T.Text,
+    source :: !(Maybe T.Text),
+    vars :: !(Map VarName T.Text),
     resolved :: !Bool
   }
   deriving stock (Eq, Generic, Show)
 
+-- | References are decoded separately from module/recipe executable records.
+data JudgementDocRef = JudgementDocRef
+  { reference :: !JudgementRef,
+    resolved :: !Bool
+  }
+  deriving stock (Eq, Generic, Show)
+
 -- | One edge of another blueprint that crossing this entry\'s edge entails.
 --
 -- @resolved@ says whether the named blueprint is listed in the /same/ registry,
@@ -129,20 +146,22 @@
           buildDocModel registryDir registry
 
 buildDocModel :: FilePath -> Registry -> IO (Either DocLoadError DocModel)
-buildDocModel registryDir Registry {repoName, repoDescription, modules, recipes, blueprints, prompts} = do
+buildDocModel registryDir Registry {repoName, repoDescription, modules, recipes, blueprints, prompts, judgements} = do
   entriesResult <-
     concatResults
       [ loadEntries (loadModuleEntry registryDir) modules,
         loadEntries (loadRecipeEntry registryDir) recipes,
         loadEntries (loadBlueprintEntry registryDir) blueprints,
-        loadEntries (loadPromptEntry registryDir) prompts
+        loadEntries (loadPromptEntry registryDir) prompts,
+        loadEntries (loadJudgementEntry registryDir) judgements
       ]
   pure $ do
     entries <- entriesResult
     let moduleNames = [entry ^. #name | entry <- entries, entry ^. #kind == DocModuleKind]
         recipeNames = [entry ^. #name | entry <- entries, entry ^. #kind == DocRecipeKind]
         blueprintNames = [entry ^. #name | entry <- entries, entry ^. #kind == DocBlueprintKind]
-        resolvedEntries = map (resolveEntryRefs moduleNames recipeNames blueprintNames) entries
+        judgementNames = [entry ^. #name | entry <- entries, entry ^. #kind == DocJudgementKind]
+        resolvedEntries = map (resolveEntryRefs moduleNames recipeNames blueprintNames judgementNames) entries
     Right
       DocModel
         { repoName = repoName,
@@ -174,31 +193,39 @@
 loadModuleEntry registryDir entry = do
   let artifactFile = registryDir </> entry ^. #path </> "module.dhall"
   result <- evalModuleFromFile artifactFile
-  pure $ case result of
-    Left err -> Left (ArtifactLoadFailed (entry ^. #name . #unModuleName) (renderModuleLoadError err))
-    Right artifact@Module {dependencies} ->
-      Right $
-        docEntryFromRegistry
-          entry
-          DocModuleKind
-          (DocModuleArtifact artifact)
-          (moduleRefs dependencies)
-          []
+  refsResult <- evalJudgementRefsFromFile artifactFile
+  pure $ do
+    refs <- either (Left . ArtifactLoadFailed (entry ^. #name . #unModuleName) . renderModuleLoadError) Right refsResult
+    loaded <- case result of
+      Left err -> Left (ArtifactLoadFailed (entry ^. #name . #unModuleName) (renderModuleLoadError err))
+      Right artifact@Module {dependencies} ->
+        Right $
+          docEntryFromRegistry
+            entry
+            DocModuleKind
+            (DocModuleArtifact artifact)
+            (moduleRefs dependencies)
+            []
+    Right (loaded & #judgementRefs .~ [JudgementDocRef ref False | ref <- refs])
 
 loadRecipeEntry :: FilePath -> RegistryEntry -> IO (Either DocLoadError DocEntry)
 loadRecipeEntry registryDir entry = do
   let artifactFile = registryDir </> entry ^. #path </> "recipe.dhall"
   result <- evalRecipeFromFile artifactFile
-  pure $ case result of
-    Left err -> Left (ArtifactLoadFailed (entry ^. #name . #unModuleName) (renderModuleLoadError err))
-    Right artifact@Recipe {modules = recipeModules} ->
-      Right $
-        docEntryFromRegistry
-          entry
-          DocRecipeKind
-          (DocRecipeArtifact artifact)
-          (moduleRefs recipeModules)
-          []
+  refsResult <- evalJudgementRefsFromFile artifactFile
+  pure $ do
+    refs <- either (Left . ArtifactLoadFailed (entry ^. #name . #unModuleName) . renderModuleLoadError) Right refsResult
+    loaded <- case result of
+      Left err -> Left (ArtifactLoadFailed (entry ^. #name . #unModuleName) (renderModuleLoadError err))
+      Right artifact@Recipe {modules = recipeModules} ->
+        Right $
+          docEntryFromRegistry
+            entry
+            DocRecipeKind
+            (DocRecipeArtifact artifact)
+            (moduleRefs recipeModules)
+            []
+    Right (loaded & #judgementRefs .~ [JudgementDocRef ref False | ref <- refs])
 
 loadBlueprintEntry :: FilePath -> RegistryEntry -> IO (Either DocLoadError DocEntry)
 loadBlueprintEntry registryDir entry = do
@@ -230,6 +257,14 @@
           []
           []
 
+loadJudgementEntry :: FilePath -> RegistryEntry -> IO (Either DocLoadError DocEntry)
+loadJudgementEntry registryDir entry = do
+  let artifactFile = registryDir </> entry ^. #path </> "judgement.dhall"
+  result <- evalJudgementFromFile artifactFile
+  pure $ case result of
+    Left err -> Left (ArtifactLoadFailed (entry ^. #name . #unModuleName) (renderModuleLoadError err))
+    Right artifact -> Right (docEntryFromRegistry entry DocJudgementKind (DocJudgementArtifact artifact) [] [])
+
 docEntryFromRegistry :: RegistryEntry -> DocKind -> DocArtifact -> [ModuleRef] -> [EntailedRef] -> DocEntry
 docEntryFromRegistry entry kind artifact refs entailed =
   DocEntry
@@ -244,13 +279,14 @@
       baseRecipe = case artifact of
         DocRecipeArtifact recipe -> (\ref -> BaseRecipeRef ref False) <$> recipe ^. #extends
         _ -> Nothing,
+      judgementRefs = [],
       entailedRefs = entailed
     }
 
 moduleRefs :: [Dependency] -> [ModuleRef]
 moduleRefs dependencies =
-  [ ModuleRef {name = moduleName ^. #unModuleName, resolved = False}
-  | moduleName <- depModuleNames dependencies
+  [ ModuleRef {name = moduleName ^. #unModuleName, source, vars, resolved = False}
+  | Dependency {module_ = moduleName, source, vars} <- dependencies
   ]
 
 entailedRefs :: [BlueprintMigration] -> [EntailedRef]
@@ -268,10 +304,11 @@
 -- | The single place that decides whether a reference points inside this
 -- registry. Module references resolve against the registry\'s module names,
 -- entailed edges against its blueprint names.
-resolveEntryRefs :: [T.Text] -> [T.Text] -> [T.Text] -> DocEntry -> DocEntry
-resolveEntryRefs moduleNames recipeNames blueprintNames entry =
+resolveEntryRefs :: [T.Text] -> [T.Text] -> [T.Text] -> [T.Text] -> DocEntry -> DocEntry
+resolveEntryRefs moduleNames recipeNames blueprintNames judgementNames entry =
   entry
-    & #moduleRefs .~ [ref & #resolved .~ ((ref ^. #name) `elem` moduleNames) | ref <- entry ^. #moduleRefs]
+    & #moduleRefs .~ [ref & #resolved .~ ((ref ^. #name) `elem` moduleNames && ref ^. #source == Nothing) | ref <- entry ^. #moduleRefs]
+    & #judgementRefs %~ map (\ref -> ref & #resolved .~ ((ref ^. #reference . #name) `elem` judgementNames && ref ^. #reference . #source == Nothing))
     & #baseRecipe %~ fmap (\ref -> ref & #resolved .~ ((ref ^. #reference . #recipe . #unRecipeName) `elem` recipeNames && ref ^. #reference . #source == Nothing))
     & #entailedRefs .~ [ref & #resolved .~ ((ref ^. #blueprint) `elem` blueprintNames) | ref <- entry ^. #entailedRefs]
 
diff --git a/src/Seihou/OKF/Docs/Render.hs b/src/Seihou/OKF/Docs/Render.hs
--- a/src/Seihou/OKF/Docs/Render.hs
+++ b/src/Seihou/OKF/Docs/Render.hs
@@ -16,15 +16,18 @@
 
 import Control.Lens ((^.))
 import Data.Aeson (Value (..))
+import Data.Aeson.Text qualified as Aeson
 import Data.Bifunctor (first)
 import Data.Either (partitionEithers)
 import Data.FileEmbed (embedStringFile)
+import Data.Foldable (toList)
 import Data.Generics.Labels ()
 import Data.List.NonEmpty (NonEmpty)
 import Data.Map.Strict (Map)
 import Data.Map.Strict qualified as Map
 import Data.Text qualified as T
 import Data.Text.IO qualified as TIO
+import Data.Text.Lazy qualified as TL
 import GHC.Generics (Generic)
 import Okf.Actor (Actor (..))
 import Okf.Bundle (BundleError, Concept, bundleInventoryOfConcepts, conceptFromDocument, writeBundle)
@@ -41,6 +44,7 @@
   )
 import Okf.Validation (BundleValidationError, ValidationProfile (..), validateBundle)
 import Seihou.Core.Expr (renderExpr)
+import Seihou.Core.Judgement qualified as J
 import Seihou.Core.Migration
   ( BlueprintMigration (..),
     EntailedEdge (..),
@@ -67,6 +71,7 @@
     RemovalAction (..),
     RemovalStep (..),
     Step (..),
+    StepLifecycle (..),
     Strategy (..),
     Validation (..),
     VarDecl (..),
@@ -315,7 +320,7 @@
         description,
         "Every artifact below is published by the `"
           <> repoName
-          <> "` registry and documented in its own concept."
+          <> "` registry and documented in its own concept. Modules, recipes, blueprints, prompts and judgements are read from declarations; generation never executes their commands or reviews."
       ]
         <> foldMap (uncurry (registryKindSection entries)) kindHeadings
     )
@@ -325,7 +330,8 @@
       [ (DocModuleKind, "Modules"),
         (DocRecipeKind, "Recipes"),
         (DocBlueprintKind, "Blueprints"),
-        (DocPromptKind, "Prompts")
+        (DocPromptKind, "Prompts"),
+        (DocJudgementKind, "Judgements")
       ]
 
 registryKindSection :: [DocEntry] -> DocKind -> T.Text -> [T.Text]
@@ -369,12 +375,14 @@
 artifactDescription (DocRecipeArtifact Recipe {description}) = description
 artifactDescription (DocBlueprintArtifact Blueprint {description}) = description
 artifactDescription (DocPromptArtifact AgentPrompt {description}) = description
+artifactDescription (DocJudgementArtifact def) = def ^. #description
 
 kindNoun :: DocKind -> T.Text
 kindNoun DocModuleKind = "module"
 kindNoun DocRecipeKind = "recipe"
 kindNoun DocBlueprintKind = "blueprint"
 kindNoun DocPromptKind = "agent prompt"
+kindNoun DocJudgementKind = "judgement"
 kindNoun DocRegistryKind = "registry"
 
 resourceFor :: T.Text -> DocEntry -> T.Text
@@ -386,6 +394,8 @@
   T.intercalate
     "\n\n"
     ( baseSections repoName entry
+        <> optionalSection "Declaration metadata" (renderDeclaredMetadata entry)
+        <> optionalSection "Judgements" (renderJudgementRefs (entry ^. #judgementRefs))
         <> kindSections entry
     )
     <> "\n"
@@ -413,7 +423,7 @@
 kindSections entry =
   case entry ^. #artifact of
     DocModuleArtifact Module {vars, exports, prompts, steps, commands, dependencies, removal, migrations} ->
-      [ section "Dependencies" (renderDependencies "This module has no dependencies." dependencies),
+      [ section "Dependencies" (renderDependencies entry "This module has no dependencies." dependencies),
         section "Variables" (renderVarDecls vars),
         section "Exports" (renderExports exports)
       ]
@@ -424,20 +434,20 @@
         <> optionalSection "Migrations" (renderMigrations migrations)
     DocRecipeArtifact Recipe {modules = recipeModules, vars, prompts, extends, moduleOverrides, promptOverrides} ->
       foldMap (\ref -> [section "Extends" (renderRecipeBase entry ref)]) extends
-        <> [section "Composes" (renderDependencies (maybe "This recipe does not compose any modules." (const "Uses its inherited members without adding modules.") extends) recipeModules)]
-        <> optionalSection "Inherited module overrides" (T.unlines ["- `" <> (override ^. #module_ . #unModuleName) <> "`" <> renderSuppliedVars (override ^. #vars) | override <- moduleOverrides])
+        <> [section "Composes" (renderDependencies entry (maybe "This recipe does not compose any modules." (const "Uses its inherited members without adding modules.") extends) recipeModules)]
+        <> optionalSection "Inherited module overrides" (T.unlines ["- " <> inlineCode (override ^. #module_ . #unModuleName) <> renderSuppliedVars (override ^. #vars) | override <- moduleOverrides])
         <> [section "Override rules" "The oldest base supplies the primary module. Child literals override ancestor literals globally by variable name; defaults remain fallbacks. Explicit module overrides replace selected keys without adding instances. Prompt overrides replace inherited questions in place; new prompts append." | extends /= Nothing]
         <> optionalSection "Variables" (renderVarDecls' vars)
         <> optionalSection "Prompts" (renderPrompts prompts)
         <> optionalSection "Inherited prompt overrides" (renderPrompts promptOverrides)
     DocBlueprintArtifact Blueprint {prompt, vars, prompts, baseModules, files, allowedTools, migrations, launch, versionProbe} ->
-      [ section "Base modules" (renderDependencies "This blueprint declares no base modules." baseModules),
+      [ section "Base modules" (renderDependencies entry "This blueprint declares no base modules." baseModules),
         section "Agent prompt" (renderAgentPrompt prompt)
       ]
         <> optionalSection "Variables" (renderVarDecls' vars)
         <> optionalSection "Prompts" (renderPrompts prompts)
         <> [section "Reference files" (renderBlueprintFiles files)]
-        <> foldMap (optionalSection "Tools" . renderTextList') allowedTools
+        <> [section "Tools" (renderTools allowedTools <> "\n\nBlueprint runs also pre-approve Seihou setup tools.")]
         <> foldMap (optionalSection "Agent launch" . renderAgentLaunch) launch
         <> foldMap (optionalSection "Version probe" . renderVersionProbe) versionProbe
         <> optionalSection
@@ -450,10 +460,62 @@
         <> optionalSection "Command variables" (renderCommandVars commandVars)
         <> optionalSection "Guidance" (renderGuidance guidance)
         <> [ section "Reference files" (renderBlueprintFiles files),
-             section "Tools" (maybe "No tool restrictions declared." renderTextList allowedTools)
+             section "Tools" (renderTools allowedTools)
            ]
         <> foldMap (optionalSection "Agent launch" . renderAgentLaunch) launch
+    DocJudgementArtifact def ->
+      [ section "Instructions" (if T.null (T.strip (def ^. #instructions)) then "No shared instructions." else fencedBlock "text" (def ^. #instructions)),
+        section "Decision semantics" "Thresholds apply to the model-reported probability of the concern, converting probability of yes according to concern direction. These probabilities are not calibrated risk estimates. Values at or below passAt may pass only with complete coverage; values at or above blockAt block; the intervening band needs review. Authored enforcement and thresholds are defaults; a consuming project's recorded policy decides. A permitted gate may clear a check, but cannot overrule review or permit a pass at its block threshold.",
+        section "Checks" (T.unlines (concatMap renderJudgementCheck (toList (def ^. #checks)))),
+        section
+          "Evidence"
+          ( "Scopes: "
+              <> T.intercalate ", " (map J.renderScopeKind (def ^. #evidence . #scopes))
+              <> ".\n"
+              <> "Include: "
+              <> listOrAll (def ^. #evidence . #include)
+              <> ". Exclude: "
+              <> listOrNone (def ^. #evidence . #exclude)
+              <> "."
+          ),
+        section "Deterministic checks" (if null (def ^. #deterministicChecks) then "None." else T.intercalate ", " (map J.renderDeterministicCheck (def ^. #deterministicChecks)) <> "."),
+        section "Coverage limits" (renderCoverageLimits (def ^. #limits))
+      ]
 
+-- | Catalog metadata remains authoritative. Preserve differing declaration
+-- metadata instead of silently dropping a stale catalog's source values.
+renderDeclaredMetadata :: DocEntry -> T.Text
+renderDeclaredMetadata entry =
+  T.unlines $
+    ["- name: " <> inlineCode name | name /= entry ^. #name]
+      <> ["- version: " <> maybe "not declared" inlineCode version | version /= entry ^. #version]
+      <> ["- description: " <> maybe "not declared" inlineCode description | description /= entry ^. #description]
+      <> ["- tags: " <> T.intercalate ", " (inlineCode <$> tags) | Just tags <- [artifactTags], tags /= entry ^. #tags]
+  where
+    (name, version, description, artifactTags) = case entry ^. #artifact of
+      DocModuleArtifact m -> (m ^. #name . #unModuleName, m ^. #version, m ^. #description, Nothing)
+      DocRecipeArtifact r -> (r ^. #name . #unRecipeName, r ^. #version, r ^. #description, Nothing)
+      DocBlueprintArtifact b -> (b ^. #name . #unModuleName, b ^. #version, b ^. #description, Just (b ^. #tags))
+      DocPromptArtifact p -> (p ^. #name . #unModuleName, p ^. #version, p ^. #description, Just (p ^. #tags))
+      DocJudgementArtifact j -> (j ^. #name, j ^. #version, j ^. #description, Nothing)
+
+renderJudgementRefs :: [JudgementDocRef] -> T.Text
+renderJudgementRefs refs =
+  T.unlines
+    [ "- "
+        <> target ref
+        <> sourceLabel (ref ^. #reference . #source)
+        <> foldMap (\condition -> " — when " <> inlineCode condition) (ref ^. #reference . #when)
+    | ref <- refs
+    ]
+  where
+    target ref
+      | ref ^. #resolved,
+        ref ^. #reference . #source == Nothing,
+        Right conceptId <- conceptIdFor DocJudgementKind (ref ^. #reference . #name) =
+          renderConceptLink conceptId (ref ^. #reference . #name)
+      | otherwise = inlineCode (ref ^. #reference . #name) <> " (not linked in this registry)"
+
 -- | Sections are joined with a blank line between them, so a body\'s own
 -- trailing newline is stripped rather than left to open a second blank line.
 section :: T.Text -> T.Text -> T.Text
@@ -476,22 +538,31 @@
 renderRecipeBase entry ref =
   let name = ref ^. #recipe . #unRecipeName
       linked = case entry ^. #baseRecipe of
-        Just base | base ^. #resolved -> case conceptIdFor DocRecipeKind name of
-          Right conceptId -> renderConceptLink conceptId name
-          Left _ -> "`" <> name <> "`"
-        _ -> "`" <> name <> "`"
-      source = maybe " (declaring registry)" (\url -> " from `" <> url <> "`") (ref ^. #source)
-      interval = maybe "" (\range -> "; version >= `" <> range ^. #minimum <> "` and < `" <> range ^. #before <> "`") (ref ^. #versionRange)
-   in linked <> source <> interval <> ". Selected from the source's default-branch HEAD; no historical tag search."
+        Just base
+          | base ^. #resolved,
+            ref ^. #source == Nothing -> case conceptIdFor DocRecipeKind name of
+              Right conceptId -> renderConceptLink conceptId name
+              Left _ -> inlineCode name
+        _ -> inlineCode name
+      source = sourceLabel (ref ^. #source)
+      interval = maybe "" (\range -> "; version >= " <> inlineCode (range ^. #minimum) <> " and < " <> inlineCode (range ^. #before)) (ref ^. #versionRange)
+   in linked <> source <> interval <> ". Selected from the source's default-branch HEAD; no historical tag search. This page documents the declared child; inherited declarations are not expanded."
 
-renderDependencies :: T.Text -> [Dependency] -> T.Text
-renderDependencies emptyMessage [] = emptyMessage
-renderDependencies _ dependencies =
+renderDependencies :: DocEntry -> T.Text -> [Dependency] -> T.Text
+renderDependencies _ emptyMessage [] = emptyMessage
+renderDependencies entry _ dependencies =
   T.unlines
-    [ "- " <> moduleLink (moduleName ^. #unModuleName) <> renderSuppliedVars vars
-    | Dependency {module_ = moduleName, vars} <- dependencies
+    [ "- " <> target dep <> sourceLabel source <> renderSuppliedVars vars
+    | dep@Dependency {source, vars} <- dependencies
     ]
+  where
+    target Dependency {module_ = moduleName, source, vars}
+      | source == Nothing, any (\ref -> ref ^. #name == moduleName ^. #unModuleName && ref ^. #source == source && ref ^. #vars == vars && ref ^. #resolved) (entry ^. #moduleRefs) = moduleLink (moduleName ^. #unModuleName)
+      | otherwise = inlineCode (moduleName ^. #unModuleName) <> " (not linked in this registry)"
 
+sourceLabel :: Maybe T.Text -> T.Text
+sourceLabel = maybe " (declaring registry)" (\source -> " from " <> inlineCode source)
+
 -- | The variable bindings the composing artifact supplies along this edge.
 renderSuppliedVars :: Map VarName T.Text -> T.Text
 renderSuppliedVars vars
@@ -500,7 +571,7 @@
       " (with "
         <> T.intercalate
           ", "
-          [ "`" <> varName <> "` = `" <> value <> "`"
+          [ inlineCode varName <> " = " <> inlineCode value
           | (VarName varName, value) <- Map.toAscList vars
           ]
         <> ")"
@@ -509,7 +580,7 @@
 moduleLink name =
   case conceptIdFor DocModuleKind name of
     Right conceptId -> renderConceptLink conceptId name
-    Left _ -> "`" <> name <> "`"
+    Left _ -> inlineCode name
 
 -- | Variables, with the type, requiredness, default, validation rule and
 -- description the declaration actually carries.
@@ -524,13 +595,13 @@
 
 renderVarDecl :: VarDecl -> T.Text
 renderVarDecl VarDecl {name = VarName varName, type_, default_, description, required, validation} =
-  "- `"
-    <> varName
-    <> "` — "
+  "- "
+    <> inlineCode varName
+    <> " — "
     <> T.intercalate
       ", "
       ( [renderVarType type_, if required then "required" else "optional"]
-          <> foldMap (\value -> ["default `" <> renderValue value <> "`"]) default_
+          <> foldMap (\value -> ["default " <> inlineCode (renderValue value)]) default_
           <> foldMap (pure . renderValidation) validation
       )
     <> foldMap (". " <>) description
@@ -541,10 +612,10 @@
 renderVarType VTInt = "integer"
 renderVarType (VTList inner) = "list of " <> renderVarType inner
 renderVarType (VTChoice choices) =
-  "one of " <> T.intercalate ", " ["`" <> choice <> "`" | choice <- choices]
+  "one of " <> T.intercalate ", " [inlineCode choice | choice <- choices]
 
 renderValidation :: Validation -> T.Text
-renderValidation (ValPattern pattern_) = "matching `" <> pattern_ <> "`"
+renderValidation (ValPattern pattern_) = "matching " <> inlineCode pattern_
 renderValidation (ValRange low high) =
   "between " <> T.pack (show low) <> " and " <> T.pack (show high)
 renderValidation (ValMinLength n) = "at least " <> T.pack (show n) <> " characters"
@@ -558,13 +629,16 @@
 renderValue (VBool True) = "true"
 renderValue (VBool False) = "false"
 renderValue (VInt n) = T.pack (show n)
-renderValue (VList values) = T.intercalate ", " (renderValue <$> values)
+renderValue (VList values) = "[" <> T.intercalate ", " (renderListValue <$> values) <> "]"
+  where
+    renderListValue (VText text) = TL.toStrict (Aeson.encodeToLazyText (String text))
+    renderListValue value = renderValue value
 
 renderExports :: [VarExport] -> T.Text
 renderExports [] = "No exports declared."
 renderExports exports =
   T.unlines
-    [ "- `" <> varName <> "`" <> foldMap (\(VarName aliasName) -> " as `" <> aliasName <> "`") alias
+    [ "- " <> inlineCode varName <> foldMap (\(VarName aliasName) -> " as " <> inlineCode aliasName) alias
     | VarExport {var = VarName varName, alias} <- exports
     ]
 
@@ -575,34 +649,40 @@
 
 renderPrompt :: Prompt -> T.Text
 renderPrompt Prompt {var = VarName varName, text, condition, choices} =
-  "- `"
-    <> varName
-    <> "` — "
-    <> T.strip text
+  "- "
+    <> inlineCode varName
+    <> " — "
+    <> inlineCode text
     <> foldMap renderChoices choices
     <> renderCondition condition
   where
     renderChoices options =
-      " (choices: " <> T.intercalate ", " ["`" <> option <> "`" | option <- options] <> ")"
+      " (choices: " <> T.intercalate ", " [inlineCode option | option <- options] <> ")"
 
 renderCondition :: Maybe Expr -> T.Text
-renderCondition = foldMap (\expr -> " — when `" <> renderExpr expr <> "`")
+renderCondition = foldMap (\expr -> " — when " <> inlineCode (renderExpr expr))
 
 renderSteps :: [Step] -> T.Text
 renderSteps steps = T.unlines (renderStep <$> steps)
 
 renderStep :: Step -> T.Text
-renderStep Step {strategy, src, dest, condition, patch} =
-  "- `"
-    <> renderStrategy strategy
-    <> "` `"
-    <> T.pack src
-    <> "` → `"
-    <> dest
-    <> "`"
+renderStep Step {strategy, src, dest, condition, patch, lifecycle} =
+  "- "
+    <> inlineCode (renderStrategy strategy)
+    <> " "
+    <> inlineCode (T.pack src)
+    <> " → "
+    <> inlineCode dest
     <> foldMap (\op -> " (" <> renderPatchOp op <> ")") patch
     <> renderCondition condition
+    <> " — "
+    <> renderStepLifecycle lifecycle
 
+renderStepLifecycle :: StepLifecycle -> T.Text
+renderStepLifecycle Managed = "Managed: owned and reconciled by Seihou updates"
+renderStepLifecycle Seed = "Seed: created once when absent, then project owned; updates and removal preserve it"
+renderStepLifecycle Checkout = "Checkout: portable symlink restored by `seihou sync` in fresh clones and worktrees"
+
 renderStrategy :: Strategy -> T.Text
 renderStrategy Copy = "Copy"
 renderStrategy Template = "Template"
@@ -621,11 +701,15 @@
 
 renderCommand :: Command -> T.Text
 renderCommand Command {run, workDir, condition, lifecycle} =
-  "- `" <> run <> "`" <> renderWorkDir workDir <> renderCondition condition <> (if lifecycle == CheckoutCommandLifecycle then " (repeatable checkout setup; seihou sync)" else "")
+  "- " <> inlineCode run <> renderWorkDir workDir <> renderCondition condition <> renderCommandLifecycle lifecycle
 
 renderWorkDir :: Maybe T.Text -> T.Text
-renderWorkDir = foldMap (\dir -> " in `" <> dir <> "`")
+renderWorkDir = foldMap (\dir -> " in " <> inlineCode dir)
 
+renderCommandLifecycle :: CommandLifecycle -> T.Text
+renderCommandLifecycle ApplicationCommand = " (application command)"
+renderCommandLifecycle CheckoutCommandLifecycle = " (repeatable checkout setup; seihou sync)"
+
 renderRemoval :: Removal -> T.Text
 renderRemoval Removal {steps, commands} =
   T.intercalate "\n" (filter (not . T.null) [renderRemovalSteps steps, renderRemovalCommands commands])
@@ -638,7 +722,7 @@
 
 renderRemovalStep :: RemovalStep -> T.Text
 renderRemovalStep RemovalStep {action, dest, src, condition} =
-  "- " <> renderRemovalAction action <> " `" <> dest <> "`" <> foldMap (\path -> " using `" <> T.pack path <> "`") src <> renderCondition condition
+  "- " <> renderRemovalAction action <> " " <> inlineCode dest <> foldMap (\path -> " using " <> inlineCode (T.pack path)) src <> renderCondition condition
 
 renderRemovalAction :: RemovalAction -> T.Text
 renderRemovalAction RemoveFileAction = "delete"
@@ -658,12 +742,13 @@
 renderMigrationOps ops = T.unlines (renderMigrationOp <$> ops)
 
 renderMigrationOp :: MigrationOp -> T.Text
-renderMigrationOp MoveFile {src, dest} = "- move `" <> T.pack src <> "` → `" <> T.pack dest <> "`"
-renderMigrationOp MoveDir {src, dest} = "- move directory `" <> T.pack src <> "` → `" <> T.pack dest <> "`"
-renderMigrationOp DeleteFile {path} = "- delete `" <> T.pack path <> "`"
-renderMigrationOp DeleteDir {path} = "- delete directory `" <> T.pack path <> "`"
+renderMigrationOp MoveFile {src, dest} = "- move " <> inlineCode (T.pack src) <> " → " <> inlineCode (T.pack dest)
+renderMigrationOp MoveDir {src, dest} = "- move directory " <> inlineCode (T.pack src) <> " → " <> inlineCode (T.pack dest)
+renderMigrationOp DeleteFile {path} = "- delete " <> inlineCode (T.pack path)
+renderMigrationOp DeleteDir {path} = "- delete directory " <> inlineCode (T.pack path)
+renderMigrationOp RemoveLine {path, line} = "- remove exact line " <> inlineCode line <> " from " <> inlineCode (T.pack path) <> "; removes every matching logical line, excluding its LF/CRLF terminator from comparison; preserves nonmatching bytes. Missing files or matches are no-ops."
 renderMigrationOp RunCommand {run, workDir} =
-  "- run `" <> run <> "`" <> foldMap (\dir -> " in `" <> T.pack dir <> "`") workDir
+  "- run " <> inlineCode run <> foldMap (\dir -> " in " <> inlineCode (T.pack dir)) workDir
 
 -- | Blueprint migration edges, each with the guidance the agent is given and
 -- the other blueprints' edges that crossing this one entails.
@@ -703,7 +788,7 @@
       "- `" <> blueprint <> "` `" <> from <> "` → `" <> to <> "` (declared outside this registry)"
 
 renderAgentLaunch :: AgentLaunch -> T.Text
-renderAgentLaunch AgentLaunch {provider, model, effort} =
+renderAgentLaunch AgentLaunch {provider, model, effort, mode} =
   case declared of
     [] -> ""
     _ ->
@@ -713,15 +798,15 @@
            \invoking user's configuration files do not.\n"
   where
     declared =
-      foldMap (\value -> ["- provider: `" <> value <> "`"]) provider
-        <> foldMap (\value -> ["- model: `" <> value <> "`"]) model
-        <> foldMap (\value -> ["- effort: `" <> value <> "`"]) effort
+      foldMap (\value -> ["- provider: " <> inlineCode value]) provider
+        <> foldMap (\value -> ["- model: " <> inlineCode value]) model
+        <> foldMap (\value -> ["- effort: " <> inlineCode value]) effort
+        <> foldMap (\value -> ["- mode: " <> inlineCode value <> " (reserved; ignored)"]) mode
 
 renderVersionProbe :: T.Text -> T.Text
 renderVersionProbe probe =
-  "```bash\n"
-    <> T.strip probe
-    <> "\n```\n\nSeihou reads no package-manager format of its own, so this \
+  fencedBlock "bash" probe
+    <> "\n\nSeihou reads no package-manager format of its own, so this \
        \author-declared command is how it discovers which version of this \
        \blueprint's library the project currently declares; its output supplies \
        \the default `--to` for `seihou agent migrate` (ADR 0009 in the seihou \
@@ -732,11 +817,10 @@
 
 renderCommandVar :: CommandVar -> T.Text
 renderCommandVar CommandVar {name = VarName varName, run, workDir, condition, trim, maxBytes} =
-  "- `"
-    <> varName
-    <> "` — runs `"
-    <> run
-    <> "`"
+  "- "
+    <> inlineCode varName
+    <> " — runs "
+    <> inlineCode run
     <> renderWorkDir workDir
     <> ", "
     <> (if trim then "trimmed" else "untrimmed")
@@ -760,32 +844,45 @@
 renderAgentPrompt prompt
   | T.null (T.strip prompt) = "No prompt text provided."
   | otherwise =
-      firstParagraph prompt <> "\n\n```text\n" <> T.strip prompt <> "\n```"
+      fencedBlock "text" prompt
 
 renderBlueprintFiles :: [BlueprintFile] -> T.Text
 renderBlueprintFiles [] = "No reference files declared."
 renderBlueprintFiles files =
   T.unlines
-    [ "- `" <> T.pack src <> "`" <> maybe "" (" - " <>) description
+    [ "- " <> inlineCode (T.pack src) <> maybe "" (" - " <>) description
     | BlueprintFile {src, description} <- files
     ]
 
-renderTextList :: [T.Text] -> T.Text
-renderTextList [] = "No tool restrictions declared."
-renderTextList values = renderTextList' values
+renderTools :: Maybe [T.Text] -> T.Text
+renderTools Nothing = "No additional tools declared."
+renderTools (Just []) = "No additional tools declared (explicit empty allowlist)."
+renderTools (Just tools) = renderTextList' tools
 
 renderTextList' :: [T.Text] -> T.Text
-renderTextList' values = T.unlines ["- `" <> value <> "`" | value <- values]
+renderTextList' values = T.unlines ["- " <> inlineCode value | value <- values]
 
-firstParagraph :: T.Text -> T.Text
-firstParagraph text =
-  case T.splitOn "\n\n" text of
-    [] -> "No prompt text provided."
-    paragraph : _ ->
-      case T.strip paragraph of
-        "" -> "No prompt text provided."
-        stripped -> stripped
+-- | Choose delimiters from the source data, so literals cannot close their own
+-- span or fence. Inline multiline text is labelled JSON to retain exact breaks.
+inlineCode :: T.Text -> T.Text
+inlineCode value
+  | T.null value || T.all (== ' ') value || T.any (`elem` ['\n', '\r']) value = codeSpan (TL.toStrict (Aeson.encodeToLazyText (String value))) <> " (JSON string)"
+  | otherwise = codeSpan value
 
+codeSpan :: T.Text -> T.Text
+codeSpan value = delimiter <> padding <> value <> padding <> delimiter
+  where
+    delimiter = T.replicate (maxBackticks value + 1) "`"
+    padding = if T.null value || T.isPrefixOf "`" value || T.isSuffixOf "`" value || T.isPrefixOf " " value || T.isSuffixOf " " value then " " else ""
+
+fencedBlock :: T.Text -> T.Text -> T.Text
+fencedBlock language value = delimiter <> language <> "\n" <> value <> (if T.isSuffixOf "\n" value then "" else "\n") <> delimiter
+  where
+    delimiter = T.replicate (max 3 (maxBackticks value + 1)) "`"
+
+maxBackticks :: T.Text -> Int
+maxBackticks value = maximum (0 : [T.length run | run <- T.group value, T.isPrefixOf "`" run])
+
 conceptIdTextFor :: DocKind -> T.Text -> T.Text
 conceptIdTextFor kind name = kindDir kind <> "/" <> name
 
@@ -794,6 +891,7 @@
 kindDir DocRecipeKind = "recipes"
 kindDir DocBlueprintKind = "blueprints"
 kindDir DocPromptKind = "prompts"
+kindDir DocJudgementKind = "judgements"
 kindDir DocRegistryKind = "registry"
 
 typeFor :: DocKind -> T.Text
@@ -801,4 +899,43 @@
 typeFor DocRecipeKind = "SeihouRecipe"
 typeFor DocBlueprintKind = "SeihouBlueprint"
 typeFor DocPromptKind = "SeihouPrompt"
+typeFor DocJudgementKind = "SeihouJudgement"
 typeFor DocRegistryKind = "SeihouRegistry"
+
+renderJudgementCheck :: J.JudgementCheck -> [T.Text]
+renderJudgementCheck c =
+  [ "### " <> c ^. #name,
+    inlineCode (c ^. #question),
+    "Enforcement: " <> J.renderEnforcement (c ^. #enforcement) <> ". Concern when the answer is " <> J.renderAnswer (c ^. #concernWhen) <> ".",
+    "Review: " <> renderThresholds (c ^. #review) <> "; findings block from " <> J.renderSeverity (c ^. #blockingSeverity) <> " severity.",
+    "Gate: useGate = " <> (if c ^. #useGate then "true" else "false (disabled)") <> "; " <> renderThresholds (c ^. #gate) <> "."
+  ]
+    <> foldMap (\meaning -> ["Yes means: " <> inlineCode meaning]) (c ^. #yesMeans)
+    <> foldMap (\meaning -> ["No means: " <> inlineCode meaning]) (c ^. #noMeans)
+    <> ["- Category " <> inlineCode (cat ^. #name) <> ": " <> J.renderSeverity (cat ^. #severity) <> foldMap (\description -> " — " <> inlineCode description) (cat ^. #description) | cat <- c ^. #categories]
+    <> [""]
+
+renderThresholds :: J.Thresholds -> T.Text
+renderThresholds thresholds = "passAt = " <> J.renderProbability (thresholds ^. #passAt) <> ", blockAt = " <> J.renderProbability (thresholds ^. #blockAt)
+
+renderCoverageLimits :: J.CoverageLimits -> T.Text
+renderCoverageLimits limits =
+  T.unlines
+    [ "- " <> field <> ": " <> T.pack (show value)
+    | (field, value) <-
+        [ ("maxFileBytes", limits ^. #maxFileBytes),
+          ("maxChunkBytes", limits ^. #maxChunkBytes),
+          ("maxChunks", limits ^. #maxChunks),
+          ("maxAttempts", limits ^. #maxAttempts),
+          ("timeoutSeconds", limits ^. #timeoutSeconds),
+          ("maxFindings", limits ^. #maxFindings)
+        ]
+    ]
+
+listOrAll :: [T.Text] -> T.Text
+listOrAll [] = "every path"
+listOrAll xs = T.intercalate ", " (inlineCode <$> xs)
+
+listOrNone :: [T.Text] -> T.Text
+listOrNone [] = "none"
+listOrNone xs = T.intercalate ", " (inlineCode <$> xs)
diff --git a/src/Seihou/OKF/Extension.hs b/src/Seihou/OKF/Extension.hs
--- a/src/Seihou/OKF/Extension.hs
+++ b/src/Seihou/OKF/Extension.hs
@@ -10,6 +10,7 @@
 import Options.Applicative
 import Options.Applicative.Help.Pretty (pretty, vsep)
 import Seihou.OKF.Extension.Docs (DocsOpts (..), handleDocs)
+import Seihou.OKF.Extension.Version (extensionVersion)
 
 data Command
   = Docs DocsOpts
@@ -28,7 +29,7 @@
 opts :: ParserInfo Command
 opts =
   info
-    (commandParser <**> helper)
+    (commandParser <**> helper <**> infoOption ("seihou-okf-extension " <> T.unpack extensionVersion) (long "version" <> help "Show extension version"))
     ( fullDesc
         <> progDesc "Generate OKF documentation bundles for Seihou registries"
         <> header "seihou-okf-extension - OKF documentation extension for Seihou"
@@ -42,15 +43,16 @@
 docsInfo :: ParserInfo Command
 docsInfo =
   info
-    (Docs <$> docsParser <**> helper)
+    (Docs <$> docsParser)
     ( fullDesc
         <> progDesc "Generate OKF docs for a Seihou registry"
         <> footerDoc
           ( Just $
               vsep
                 [ pretty ("Reads seihou-registry.dhall, loads each listed artifact, validates the OKF bundle," :: String),
-                  pretty ("and writes one Markdown concept document per registry entry." :: String),
-                  pretty ("Smoke value length: " <> show (T.length okfSmoke) :: String)
+                  pretty ("and writes one concept per module, recipe, blueprint, prompt and judgement, plus the registry overview." :: String),
+                  pretty ("From a fresh checkout: seihou-okf-extension docs. Regenerate: add --force." :: String),
+                  pretty ("No dependency installation or command execution is needed. Invalid input preserves existing output." :: String)
                 ]
           )
     )
diff --git a/src/Seihou/OKF/Extension/Docs.hs b/src/Seihou/OKF/Extension/Docs.hs
--- a/src/Seihou/OKF/Extension/Docs.hs
+++ b/src/Seihou/OKF/Extension/Docs.hs
@@ -25,7 +25,9 @@
   ( FieldCondition (..),
     FieldPath (..),
     FieldPathSegment (..),
+    ProfileDefinitionError (..),
     ProfileViolation (..),
+    ValueSelector (..),
     renderCardinalityName,
     renderFieldFormatName,
   )
@@ -40,10 +42,12 @@
     doesPathExist,
     listDirectory,
     removeDirectoryRecursive,
+    renameDirectory,
   )
 import System.Exit (exitFailure)
-import System.FilePath ((</>))
+import System.FilePath (takeDirectory, (</>))
 import System.IO (stderr)
+import System.IO.Temp (withTempDirectory)
 
 data DocsOpts = DocsOpts
   { dir :: !FilePath,
@@ -97,18 +101,18 @@
                   | not (null validationProblems) ->
                       pure (Left (renderMany renderBundleValidationError validationProblems))
                   | otherwise -> do
-                      -- Check the house profile before touching the output
-                      -- directory, so a violating run leaves whatever was
-                      -- there untouched rather than clearing it first.
-                      profileProblems <- checkDocProfile (renderOptionsFor opts) concepts
-                      if not (null profileProblems)
-                        then pure (Left (renderMany renderDocBundleError profileProblems))
-                        else do
-                          prepareOutputDirectory (opts ^. #out)
-                          writeResult <- writeDocBundle (renderOptionsFor opts) (opts ^. #out) model
-                          pure $ case writeResult of
-                            Left errors -> Left (renderMany renderDocBundleError errors)
-                            Right () -> Right ("Wrote " <> T.pack (show (length concepts)) <> " concepts to " <> T.pack (opts ^. #out))
+                      -- Complete validation, profile copying and index writing
+                      -- before replacing output. In particular, --profile may
+                      -- name the descriptor inside the bundle being replaced.
+                      let parent = takeDirectory (opts ^. #out)
+                      createDirectoryIfMissing True parent
+                      withTempDirectory parent ".seihou-okf-docs" $ \staging -> do
+                        writeResult <- writeDocBundle (renderOptionsFor opts) staging model
+                        case writeResult of
+                          Left errors -> pure (Left (renderMany renderDocBundleError errors))
+                          Right () -> do
+                            replaceOutputDirectory staging (opts ^. #out)
+                            pure (Right ("Wrote " <> T.pack (show (length concepts)) <> " concepts to " <> T.pack (opts ^. #out)))
 
 handleDocs :: DocsOpts -> IO ()
 handleDocs opts = do
@@ -135,11 +139,11 @@
             then pure (Right ())
             else pure (Left ("output directory is not empty: " <> T.pack (opts ^. #out) <> "; pass --force to overwrite"))
 
-prepareOutputDirectory :: FilePath -> IO ()
-prepareOutputDirectory outDir = do
+replaceOutputDirectory :: FilePath -> FilePath -> IO ()
+replaceOutputDirectory staging outDir = do
   exists <- doesDirectoryExist outDir
   when exists (removeDirectoryRecursive outDir)
-  createDirectoryIfMissing True outDir
+  renameDirectory staging outDir
 
 renderDocLoadError :: DocLoadError -> T.Text
 renderDocLoadError (RegistryNotFound path) =
@@ -158,7 +162,7 @@
   "could not read the house profile descriptor: " <> err
 renderDocBundleError (DocBundleProfileInvalid errs) =
   "the house profile descriptor does not compile: "
-    <> T.intercalate "; " (T.pack . show <$> NonEmpty.toList errs)
+    <> T.intercalate "; " (renderProfileDefinitionError <$> NonEmpty.toList errs)
 renderDocBundleError (DocBundleProfileViolation violation) =
   "house profile: " <> renderProfileViolation violation
 
@@ -262,6 +266,20 @@
 -- lives in the @okf-cli@ package, which this repository does not depend on.
 -- Total by construction, for the reason 'renderBundleValidationError' gives.
 renderProfileViolation :: ProfileViolation -> T.Text
+renderProfileViolation (ConstraintPresenceMissing conceptId path condition) =
+  at conceptId <> "constraint requires field " <> renderFieldPath path <> renderFieldCondition condition
+renderProfileViolation (ConstraintAlternativeMissing conceptId paths condition) =
+  at conceptId <> "constraint requires one of " <> T.intercalate ", " (renderFieldPath <$> paths) <> renderFieldCondition condition
+renderProfileViolation (TimestampOrderViolation conceptId before after) =
+  at conceptId <> renderFieldPath before <> " must precede " <> renderFieldPath after
+renderProfileViolation (TextOperandMismatch conceptId path value) =
+  at conceptId <> renderFieldPath path <> " must hold text for this constraint, but holds " <> renderJson value
+renderProfileViolation (MembershipValueMissing conceptId path value selector) =
+  at conceptId <> renderFieldPath path <> " holds " <> value <> ", which is absent from " <> renderValueSelector selector
+renderProfileViolation (DuplicateSelectedValue conceptId path value other) =
+  at conceptId <> renderFieldPath path <> " repeats " <> value <> " from " <> renderFieldPath other
+renderProfileViolation (NestedFieldNotInProfile conceptId path) =
+  at conceptId <> "nested field " <> renderFieldPath path <> " is not one this profile declares"
 renderProfileViolation (TypeNotInProfile conceptId conceptType) =
   at conceptId <> "type " <> conceptType <> " is not one this profile declares"
 renderProfileViolation (MissingProfileField conceptId key condition) =
@@ -382,3 +400,205 @@
 
 renderJson :: Value -> T.Text
 renderJson = TL.toStrict . TLB.toLazyText . Aeson.encodeToTextBuilder
+
+renderValueSelector :: ValueSelector -> T.Text
+renderValueSelector ValueSelector {field, member} = field <> foldMap ("[]." <>) member
+
+-- Adapted from the diagnostic contract of mori://shinzui/okf/packages/okf-cli.
+renderProfileDefinitionError :: ProfileDefinitionError -> T.Text
+renderProfileDefinitionError = \case
+  InvalidConceptConstraint scope parent sel reason -> renderScope scope <> ": invalid constraint " <> maybe "" (<> ".") parent <> renderValueSelector sel <> ": " <> reason
+  DuplicateTypeRule ctype -> "duplicate type rule: " <> ctype
+  DuplicateFieldRule scope listName key ->
+    renderScope scope <> ": duplicate " <> listName <> " field: " <> key
+  ConflictingFieldRequirement scope key ->
+    renderScope scope <> ": field appears in more than one of required, recommended, and optional: " <> key
+  UnsatisfiableVocabulary scope key profileValues typeValues ->
+    renderScope scope
+      <> ": disjoint allowed values for "
+      <> key
+      <> " (profile: ["
+      <> T.intercalate ", " profileValues
+      <> "], type: ["
+      <> T.intercalate ", " typeValues
+      <> "])"
+  ConflictingCardinality scope key profileCardinality typeCardinality ->
+    renderScope scope
+      <> ": conflicting cardinality for "
+      <> key
+      <> " (profile: "
+      <> renderCardinalityName profileCardinality
+      <> ", type: "
+      <> renderCardinalityName typeCardinality
+      <> ")"
+  ElementFieldsRequireList scope fieldPath actualCardinality ->
+    renderScope scope
+      <> ": elementFields at "
+      <> renderFieldPath fieldPath
+      <> " requires list cardinality, found: "
+      <> renderCardinalityName actualCardinality
+  ObjectFieldsRequireObjectShape scope fieldPath actualCardinality ->
+    renderScope scope
+      <> ": objectFields at "
+      <> renderFieldPath fieldPath
+      <> " cannot be combined with cardinality "
+      <> renderCardinalityName actualCardinality
+  InvalidFormatParameter fieldPath fieldFormat parameter ->
+    "invalid parameter for format "
+      <> renderFieldFormatName fieldFormat
+      <> " at "
+      <> renderFieldPath fieldPath
+      <> ": "
+      <> parameter
+  ConflictingFieldFormat fieldPath profileFormat typeFormat ->
+    "conflicting formats for "
+      <> renderFieldPath fieldPath
+      <> " (profile: "
+      <> renderFieldFormatName profileFormat
+      <> ", type: "
+      <> renderFieldFormatName typeFormat
+      <> ")"
+  EmptyConditionValues scope target source ->
+    renderConditionDefinition scope target source <> " has an empty hasValue list"
+  ConditionFieldNotDeclared scope target source ->
+    renderConditionDefinition scope target source <> " names an undeclared source field"
+  ConditionFieldNotScalar scope target source actualCardinality ->
+    renderConditionDefinition scope target source
+      <> " requires scalar cardinality, found: "
+      <> renderCardinalityName actualCardinality
+  ConditionFieldOpenVocabulary scope target source ->
+    renderConditionDefinition scope target source <> " requires a non-empty allowedValues vocabulary"
+  ConditionFieldHasUnreachableValues scope target source unreachable allowed ->
+    renderConditionDefinition scope target source
+      <> " contains unreachable values ["
+      <> T.intercalate ", " unreachable
+      <> "]; source allows ["
+      <> T.intercalate ", " allowed
+      <> "]"
+  SelfConditionalField scope target ->
+    renderScope scope <> ": field cannot condition its own presence: " <> renderFieldPath target
+  InvalidReferencePrefix scope target prefix ->
+    renderScope scope <> ": invalid local reference prefix at " <> renderFieldPath target <> ": " <> prefix
+  ReferencePrefixNotDeclared scope target prefix ->
+    renderScope scope
+      <> ": local reference prefix at "
+      <> renderFieldPath target
+      <> " is not declared by any type: "
+      <> prefix
+  ReferenceRequiresIdField scope target ->
+    renderScope scope <> ": reference at " <> renderFieldPath target <> " requires profile idField"
+  InvalidExternalReferenceScheme scope target scheme ->
+    renderScope scope <> ": invalid external reference scheme at " <> renderFieldPath target <> ": " <> scheme
+  ConflictingReferencePrefix ctype target profilePrefix typePrefix ->
+    "type "
+      <> ctype
+      <> " frontmatter: conflicting local reference prefixes for "
+      <> renderFieldPath target
+      <> " (profile: "
+      <> profilePrefix
+      <> ", type: "
+      <> typePrefix
+      <> ")"
+  ReferenceWithFormat scope target fieldFormat ->
+    renderScope scope
+      <> ": reference at "
+      <> renderFieldPath target
+      <> " cannot also declare format "
+      <> renderFieldFormatName fieldFormat
+  OptionalFieldWithCondition scope target ->
+    renderScope scope
+      <> ": optional field cannot carry a when condition: "
+      <> renderFieldPath target
+  PathReferenceWithHandleReference scope target ->
+    renderScope scope
+      <> ": path at "
+      <> renderFieldPath target
+      <> " cannot also declare a document reference; a value is resolved as one or the other"
+  InvalidExternalUriPattern scope target patternText detail ->
+    renderScope scope
+      <> ": invalid external URI pattern at "
+      <> renderFieldPath target
+      <> ": "
+      <> patternText
+      <> " ("
+      <> detail
+      <> ")"
+  ConflictingExternalUriPatterns ctype target profilePattern typePattern ->
+    "type "
+      <> ctype
+      <> " frontmatter: conflicting external URI patterns for "
+      <> renderFieldPath target
+      <> " (profile: "
+      <> profilePattern
+      <> ", type: "
+      <> typePattern
+      <> ")"
+  UniqueByRequiresElementFields scope target key ->
+    renderScope scope
+      <> ": uniqueBy at "
+      <> renderFieldPath target
+      <> " names "
+      <> key
+      <> " but the field has no elementFields"
+  UniqueByFieldNotDeclared scope target ->
+    renderScope scope <> ": uniqueBy field is not declared at " <> renderFieldPath target
+  UniqueByFieldNotUnconditionallyRequired scope target ->
+    renderScope scope <> ": uniqueBy field must be unconditionally required at " <> renderFieldPath target
+  UniqueByFieldNotScalar scope target actualCardinality ->
+    renderScope scope
+      <> ": uniqueBy field must be scalar at "
+      <> renderFieldPath target
+      <> ", found: "
+      <> renderCardinalityName actualCardinality
+  ConflictingUniqueBy ctype target profileKey typeKey ->
+    "type "
+      <> ctype
+      <> " frontmatter: conflicting uniqueBy keys for "
+      <> renderFieldPath target
+      <> " (profile: "
+      <> profileKey
+      <> ", type: "
+      <> typeKey
+      <> ")"
+  -- Each version message names *both* halves of the contradiction. One that said
+  -- only "field requires OKF 0.2" would leave the author hunting for where the
+  -- version is declared, which is the other end of the file.
+  InvalidProfileOkfVersion rawVersion ->
+    "okfVersion is not <major>.<minor>: " <> rawVersion
+  ProfileOkfVersionNotUnderstood rawVersion ->
+    "okfVersion "
+      <> rawVersion
+      <> " names an OKF major version this okf does not implement (supported: "
+      <> "0.2"
+      <> ")"
+  InvalidRequiredBundleVersion rawVersion ->
+    "requireBundleVersion is not <major>.<minor>: " <> rawVersion
+  FieldSupersededInOkfVersion scope target declared supersededIn ->
+    renderScope scope
+      <> ": declared okfVersion "
+      <> declared
+      <> " supersedes the frontmatter key "
+      <> renderFieldPath target
+      <> " (OKF "
+      <> supersededIn
+      <> "); move it to the optional list"
+  FormatRequiresOkfVersion scope target fieldFormat declared introducedIn ->
+    renderScope scope
+      <> ": declared okfVersion "
+      <> declared
+      <> " does not support the format "
+      <> renderFieldFormatName fieldFormat
+      <> " at "
+      <> renderFieldPath target
+      <> ", which OKF "
+      <> introducedIn
+      <> " introduced"
+  where
+    renderScope Nothing = "profile frontmatter"
+    renderScope (Just ctype) = "type " <> ctype <> " frontmatter"
+    renderConditionDefinition scope target source =
+      renderScope scope
+        <> ": condition for "
+        <> renderFieldPath target
+        <> " on "
+        <> renderFieldPath source
diff --git a/test/Seihou/OKF/Docs/ModelSpec.hs b/test/Seihou/OKF/Docs/ModelSpec.hs
--- a/test/Seihou/OKF/Docs/ModelSpec.hs
+++ b/test/Seihou/OKF/Docs/ModelSpec.hs
@@ -1,8 +1,9 @@
-module Seihou.OKF.Docs.ModelSpec (tests) where
+module Seihou.OKF.Docs.ModelSpec (tests, writeCurrentFixtureRegistry) where
 
 import Control.Lens ((^.))
 import Data.Generics.Labels ()
 import Data.List (find)
+import Data.Map.Strict qualified as Map
 import Data.Maybe (fromMaybe)
 import Data.Text qualified as T
 import Data.Text.IO qualified as TIO
@@ -20,7 +21,7 @@
 spec :: Spec
 spec = do
   describe "loadDocModel" $ do
-    it "loads all four registry entry kinds from a fixture registry" $ do
+    it "loads all five registry entry kinds from a fixture registry" $ do
       withFixtureRegistry $ \registryDir -> do
         model <- shouldLoad registryDir
         (model ^. #repoName) `shouldBe` "fixture-registry"
@@ -28,6 +29,7 @@
         length (entriesByKind DocRecipeKind model) `shouldBe` 1
         length (entriesByKind DocBlueprintKind model) `shouldBe` 2
         length (entriesByKind DocPromptKind model) `shouldBe` 1
+        length (entriesByKind DocJudgementKind model) `shouldBe` 1
 
     it "keeps catalog metadata from the registry entry" $ do
       withFixtureRegistry $ \registryDir -> do
@@ -42,21 +44,21 @@
       withFixtureRegistry $ \registryDir -> do
         model <- shouldLoad registryDir
         let entry = requireEntry "app" model
-        (entry ^. #moduleRefs) `shouldContain` [ModuleRef "base" True]
+        (entry ^. #moduleRefs) `shouldContain` [ModuleRef "base" Nothing Map.empty True]
 
     it "marks module dependencies that do not resolve inside the registry" $ do
       withFixtureRegistry $ \registryDir -> do
         model <- shouldLoad registryDir
         let entry = requireEntry "dangling" model
-        (entry ^. #moduleRefs) `shouldBe` [ModuleRef "missing" False]
+        (entry ^. #moduleRefs) `shouldBe` [ModuleRef "missing" Nothing Map.empty False]
 
     it "captures recipe and blueprint module references" $ do
       withFixtureRegistry $ \registryDir -> do
         model <- shouldLoad registryDir
         let recipe = requireEntry "app-recipe" model
             blueprint = requireEntry "app-blueprint" model
-        (recipe ^. #moduleRefs) `shouldMatchList` [ModuleRef "base" True, ModuleRef "app" True]
-        (blueprint ^. #moduleRefs) `shouldBe` [ModuleRef "base" True]
+        (recipe ^. #moduleRefs) `shouldMatchList` [ModuleRef "base" Nothing Map.empty True, ModuleRef "app" Nothing Map.empty True]
+        (blueprint ^. #moduleRefs) `shouldBe` [ModuleRef "base" Nothing Map.empty True]
 
     it "retains an external base reference without fetching it or requiring installed members" $
       withFixtureRegistry $ \registryDir -> do
@@ -70,6 +72,20 @@
             base ^. #reference . #source `shouldBe` Just "https://example.invalid/base.git"
             base ^. #reference . #recipe `shouldBe` "external"
 
+    it "retains source-qualified edges and judgement references without resolving local shadows" $
+      withSystemTempDirectory "seihou-current-model" $ \registryDir -> do
+        writeCurrentFixtureRegistry registryDir
+        model <- shouldLoad registryDir
+        let app = requireEntry "app" model
+            recipe = requireEntry "app-recipe" model
+            blueprint = requireEntry "app-blueprint" model
+        app ^. #moduleRefs `shouldBe` [ModuleRef "base" (Just "https://example.invalid/modules.git") (Map.singleton "literal" "a|`b`") False]
+        map (^. #resolved) (app ^. #judgementRefs) `shouldBe` [True, False, False]
+        map (^. #resolved) (recipe ^. #judgementRefs) `shouldBe` [True, False, False]
+        map (^. #resolved) (recipe ^. #moduleRefs) `shouldBe` [True, False]
+        map (^. #resolved) (blueprint ^. #moduleRefs) `shouldBe` [False]
+        fmap (^. #resolved) (recipe ^. #baseRecipe) `shouldBe` Just False
+
     it "resolves an entailed edge naming a blueprint in the same registry" $ do
       withFixtureRegistry $ \registryDir -> do
         model <- shouldLoad registryDir
@@ -147,6 +163,8 @@
   writeBlueprint registryDir
   writeEntailingBlueprint registryDir
   writePrompt registryDir
+  createDirectoryIfMissing True (registryDir </> "judgements/docs-review")
+  writeFile (registryDir </> "judgements/docs-review/judgement.dhall") judgementDhall
 
 writeModule :: FilePath -> FilePath -> String -> [String] -> String -> IO ()
 writeModule registryDir relDir name dependencies description = do
@@ -194,6 +212,7 @@
   \  , { name = \"entailing-blueprint\", version = Some \"0.1.0\", path = \"blueprints/entailing-blueprint\", description = Some \"Entailing blueprint\", tags = [] : List Text }\n\
   \  ]\n\
   \, prompts = [ { name = \"review\", version = Some \"0.1.0\", path = \"prompts/review\", description = Some \"Review prompt\", tags = [ \"prompt\" ] } ]\n\
+  \, judgements = [ { name = \"docs-review\", version = Some \"1.0.0\", path = \"judgements/docs-review\", description = Some \"Docs review\", tags = [] : List Text } ]\n\
   \}"
 
 moduleDhall :: String -> [String] -> String -> String
@@ -348,3 +367,48 @@
 commandVarListType :: String
 commandVarListType =
   "List { name : Text, run : Text, workDir : Optional Text, when : Optional Text, trim : Bool, maxBytes : Optional Natural }"
+
+-- | A judgement written out in full, without the schema package.
+judgementDhall :: String
+judgementDhall =
+  "let Answer = < Yes | No > let Enforcement = < Enforced | Advisory > let Severity = < Info | Low | Medium | High | Critical >\n\
+  \let Scope = < StagedChanges | CommitRange > let Det = < SecretScan >\n\
+  \in\n\
+  \{ name = \"docs-review\", version = Some \"1.0.0\", description = Some \"Docs review\", instructions = \"Review documentation.\"\n\
+  \   , evidence = { scopes = [ Scope.StagedChanges ], include = [ \"docs/**\" ], exclude = [] : List Text }\n\
+  \   , limits = { maxFileBytes = 262144, maxChunkBytes = 49152, maxChunks = 16, maxAttempts = 3, timeoutSeconds = 120, maxFindings = 20 }\n\
+  \   , deterministicChecks = [] : List Det\n\
+  \   , checks = [ { name = \"stale-docs\", question = \"Does this change leave documented behaviour out of date?\", concernWhen = Answer.Yes, yesMeans = None Text, noMeans = None Text\n\
+  \              , categories = [ { name = \"stale\", severity = Severity.Medium, description = None Text } ], enforcement = Enforcement.Enforced, blockingSeverity = Severity.High\n\
+  \              , blockAt = 0.7, passAt = 0.3, gateBlockAt = 0.9, gatePassAt = 0.05, useGate = True } ] }\n"
+
+-- | Current declarations layered over the older standalone descriptors. None
+-- of the command strings should execute while loading or generating docs.
+writeCurrentFixtureRegistry :: FilePath -> IO ()
+writeCurrentFixtureRegistry registryDir = do
+  writeFixtureRegistry registryDir
+  overlay "modules/app/module.dhall" "let Op = < MoveFile : { src : Text, dest : Text } | MoveDir : { src : Text, dest : Text } | DeleteFile : { path : Text } | DeleteDir : { path : Text } | RemoveLine : { path : Text, line : Text } | RunCommand : { run : Text, workDir : Optional Text } >\nlet Action = < Apply : Text | Checkout : Text >\nin { dependencies = [ { module = \"base\", source = Some \"https://example.invalid/modules.git\", vars = [ {name=\"literal\",value=\"a|`b`\"} ] } ]\n, judgements = [ { name = \"docs-review\", source = None Text, when = Some \"IsSet project.name\" }, { name = \"docs-review\", source = Some \"https://example.invalid/reviews.git\", when = None Text }, { name = \"catalogue-review\", source = None Text, when = None Text } ]\n, steps = [ {strategy=\"template\",src=\"managed.tpl\",dest=\"managed\",when=None Text,patch=None Text,lifecycle=Some \"managed\"}, {strategy=\"template\",src=\"seed.tpl\",dest=\"seed\",when=None Text,patch=None Text,lifecycle=Some \"seed\"}, {strategy=\"symlink\",src=\"files\",dest=\"linked\",when=None Text,patch=None Text,lifecycle=Some \"checkout\"} ]\n, commands = [ {action=Action.Checkout \"touch SHOULD-NOT-EXIST\",workDir=None Text,when=None Text} ]\n, migrations = [ {from=\"1\",to=\"2\",ops=[Op.RemoveLine {path=\".gitignore\",line=\"exact|`literal`\"}]} ]\n}"
+  overlay "recipes/app-recipe/recipe.dhall" "{ modules = [ { module = \"base\", source = None Text, vars = [] : List {name : Text,value : Text} }, { module = \"base\", source = Some \"https://example.invalid/modules.git\", vars = [] : List {name : Text,value : Text} } ]\n, extends = Some { recipe = \"app-recipe\", source = Some \"https://example.invalid/recipes.git\", versionRange = Some {minimum=\"1\",before=\"2\"} }\n, moduleOverrides = [ { module = \"inherited\", vars = [ {name=\"literal\",value=\"a|`b`\"} ] } ]\n, promptOverrides = [ {var=\"inherited\",text=\"First line\\n```text\\nsecond | line\",when=None Text,choices=None (List Text)} ]\n, judgements = [ { name = \"docs-review\", source = None Text, when = Some \"IsSet project.name\" }, { name = \"docs-review\", source = Some \"https://example.invalid/reviews.git\", when = None Text }, { name = \"catalogue-review\", source = None Text, when = None Text } ] }"
+  overlay "blueprints/app-blueprint/blueprint.dhall" "{ baseModules = [ { module = \"base\", source = Some \"https://example.invalid/modules.git\", vars = [] : List {name : Text,value : Text} } ], allowedTools=Some ([] : List Text), prompt=\"First line\\n```text\\nsecond | line\",versionProbe=Some \"touch SHOULD-NOT-EXIST\\n```\",launch=Some {provider=Some \"test\",model=Some \"model\",effort=Some \"high\",mode=Some \"reserved\"} }"
+  let judgementPath = registryDir </> "judgements/docs-review/judgement.dhall"
+  judgement <- TIO.readFile judgementPath
+  TIO.writeFile
+    judgementPath
+    ( T.replace
+        "yesMeans = None Text"
+        "yesMeans = Some \"Documentation is stale\""
+        ( T.replace
+            "noMeans = None Text"
+            "noMeans = Some \"Documentation matches\""
+            ( T.replace
+                "description = None Text"
+                "description = Some \"Category explanation\""
+                (T.replace "useGate = True" "useGate = False" judgement)
+            )
+        )
+    )
+  where
+    overlay relative fields = do
+      let path = registryDir </> relative
+      old <- TIO.readFile path
+      TIO.writeFile path ("(" <> old <> ") // (" <> fields <> ")")
diff --git a/test/Seihou/OKF/Docs/RenderSpec.hs b/test/Seihou/OKF/Docs/RenderSpec.hs
--- a/test/Seihou/OKF/Docs/RenderSpec.hs
+++ b/test/Seihou/OKF/Docs/RenderSpec.hs
@@ -57,9 +57,12 @@
       (_, problems) <- renderOrFail testOptions wellFormedModel
       problems `shouldBe` []
 
-    it "reports a DanglingReference for an unresolved module ref" $ do
+    it "labels an unresolved module without making a dangling link" $ do
       (_, problems) <- renderOrFail testOptions danglingModel
-      problems `shouldSatisfy` any isDanglingReference
+      problems `shouldBe` []
+      body <- requireBody "modules/app" testOptions danglingModel
+      body `shouldSatisfy` T.isInfixOf "`missing` (not linked in this registry)"
+      body `shouldNotSatisfy` T.isInfixOf "](/modules/missing.md)"
 
     it "reports invalid generated concept IDs as render errors" $ do
       renderDocBundle testOptions invalidIdModel
@@ -113,7 +116,7 @@
       body `shouldSatisfy` T.isInfixOf "## Prompts"
       body
         `shouldSatisfy` T.isInfixOf
-          "- `license` — Which license? (choices: `MIT`, `BSD-3`) — when `IsSet project.name`"
+          "- `license` — `Which license?` (choices: `MIT`, `BSD-3`) — when `IsSet project.name`"
 
     it "renders generation steps with strategy, patch operation and condition" $ do
       body <- requireBody "modules/rich" testOptions richModel
@@ -144,6 +147,7 @@
       body `shouldSatisfy` T.isInfixOf "- move `old.nix` → `new.nix`"
       body `shouldSatisfy` T.isInfixOf "### 0.2.0 → 0.3.0"
       body `shouldSatisfy` T.isInfixOf "- delete directory `legacy`"
+      body `shouldSatisfy` T.isInfixOf "- remove exact line `.envrc` from `.gitignore`"
       body `shouldSatisfy` T.isInfixOf "- run `just fmt` in `."
 
     it "omits sections the module declares nothing for" $ do
@@ -285,10 +289,6 @@
         [concept] -> pure concept
         other -> expectationFailure ("Expected one concept, got " <> show (length other)) >> error "unreachable"
 
-isDanglingReference :: BundleValidationError -> Bool
-isDanglingReference DanglingReference {} = True
-isDanglingReference _ = False
-
 wellFormedModel :: DocModel
 wellFormedModel =
   DocModel
@@ -296,7 +296,7 @@
       repoDescription = Just "Fixture",
       entries =
         [ moduleEntry "base" [] "modules/base",
-          moduleEntry "app" [ModuleRef "base" True] "modules/app",
+          moduleEntry "app" [ModuleRef "base" Nothing Map.empty True] "modules/app",
           recipeEntry,
           blueprintEntry,
           promptEntry
@@ -309,7 +309,7 @@
     { repoName = "fixture",
       repoDescription = Nothing,
       entries =
-        [ moduleEntry "app" [ModuleRef "missing" False] "modules/app"
+        [ moduleEntry "app" [ModuleRef "missing" Nothing Map.empty False] "modules/app"
         ]
     }
 
@@ -344,6 +344,7 @@
       artifact = DocModuleArtifact (silentModuleArtifact artifactDescription),
       moduleRefs = [],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -385,6 +386,7 @@
       artifact = DocModuleArtifact (moduleArtifact name refs),
       moduleRefs = refs,
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -426,8 +428,9 @@
               moduleOverrides = [],
               promptOverrides = []
             },
-      moduleRefs = [ModuleRef "base" True, ModuleRef "app" True],
+      moduleRefs = [ModuleRef "base" Nothing Map.empty True, ModuleRef "app" Nothing Map.empty True],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -457,8 +460,9 @@
               launch = Nothing,
               versionProbe = Nothing
             },
-      moduleRefs = [ModuleRef "base" True],
+      moduleRefs = [ModuleRef "base" Nothing Map.empty True],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -489,6 +493,7 @@
             },
       moduleRefs = [],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -515,6 +520,7 @@
       artifact = DocModuleArtifact richModule,
       moduleRefs = [],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -607,6 +613,7 @@
               to = "0.3.0",
               ops =
                 [ DeleteDir {path = "legacy"},
+                  RemoveLine {path = ".gitignore", line = ".envrc"},
                   RunCommand {run = "just fmt", workDir = Just "."}
                 ]
             }
@@ -641,8 +648,9 @@
               moduleOverrides = [],
               promptOverrides = []
             },
-      moduleRefs = [ModuleRef "rich" True],
+      moduleRefs = [ModuleRef "rich" Nothing Map.empty True],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -660,6 +668,7 @@
       -- One entailed edge resolves inside this registry and one does not, which
       -- is the distinction the renderer has to honour.
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs =
         [ EntailedRef {blueprint = "other-blueprint", from = "1.9.0", to = "2.0.0", resolved = True},
           EntailedRef {blueprint = "kiroku", from = "1.9.0", to = "2.0.0", resolved = False}
@@ -731,6 +740,7 @@
             },
       moduleRefs = [],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
@@ -776,6 +786,7 @@
             },
       moduleRefs = [],
       baseRecipe = Nothing,
+      judgementRefs = [],
       entailedRefs = []
     }
 
diff --git a/test/Seihou/OKF/Extension/DocsSpec.hs b/test/Seihou/OKF/Extension/DocsSpec.hs
--- a/test/Seihou/OKF/Extension/DocsSpec.hs
+++ b/test/Seihou/OKF/Extension/DocsSpec.hs
@@ -1,15 +1,23 @@
 module Seihou.OKF.Extension.DocsSpec (tests) where
 
-import Control.Lens ((&), (.~), (?~))
+import Control.Lens ((&), (.~), (?~), (^.))
+import Control.Monad (forM)
+import Data.Aeson (Value (..))
 import Data.Generics.Labels ()
+import Data.List (sort)
+import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text qualified as T
 import Data.Text.IO qualified as TIO
+import Dhall qualified
 import Okf.Bundle qualified as Okf
+import Okf.ConceptId qualified as Okf
 import Okf.Index qualified as Okf
+import Okf.Profile qualified as Profile
 import Okf.Validation qualified as Okf
-import Seihou.OKF.Docs.Render (builtinProfileDescriptor)
+import Seihou.OKF.Docs.ModelSpec (writeCurrentFixtureRegistry)
+import Seihou.OKF.Docs.Render (DocBundleError (..), builtinProfileDescriptor)
 import Seihou.OKF.Extension.Docs
-import System.Directory (createDirectoryIfMissing, doesFileExist, doesPathExist)
+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, doesPathExist, listDirectory)
 import System.FilePath ((</>))
 import System.IO.Temp (withSystemTempDirectory)
 import Test.Hspec
@@ -21,6 +29,26 @@
 
 spec :: Spec
 spec = do
+  describe "OKF 0.13 diagnostics" $ do
+    it "names the field and violated constraint in every new profile diagnostic" $ do
+      Right conceptId <- pure (Okf.parseConceptId "modules/base")
+      let field = Profile.FieldPath (Profile.FieldName "generated" :| [Profile.FieldName "at"])
+          other = Profile.FieldPath (Profile.FieldName "reviewed" :| [])
+          selector = Profile.ValueSelector "sources" (Just "id")
+          cases =
+            [ (Profile.ConstraintPresenceMissing conceptId field Nothing, "constraint requires field generated.at"),
+              (Profile.ConstraintAlternativeMissing conceptId [field, other] Nothing, "constraint requires one of generated.at, reviewed"),
+              (Profile.TimestampOrderViolation conceptId field other, "generated.at must precede reviewed"),
+              (Profile.TextOperandMismatch conceptId field (Number 7), "must hold text for this constraint, but holds 7"),
+              (Profile.ValueFormatMismatch conceptId field Profile.FiniteDecimal (String "0.5"), "must be finite-decimal"),
+              (Profile.MembershipValueMissing conceptId field "missing" selector, "absent from sources[].id"),
+              (Profile.DuplicateSelectedValue conceptId field "duplicate" other, "repeats duplicate from reviewed"),
+              (Profile.NestedFieldNotInProfile conceptId field, "nested field generated.at is not one this profile declares")
+            ]
+      mapM_ (\(problem, expected) -> renderDocBundleError (DocBundleProfileViolation problem) `shouldSatisfy` T.isInfixOf expected) cases
+      renderDocBundleError (DocBundleProfileInvalid (Profile.InvalidConceptConstraint Nothing Nothing selector "undeclared source" :| []))
+        `shouldSatisfy` T.isInfixOf "invalid constraint sources[].id: undeclared source"
+
   describe "runDocs" $ do
     it "writes and validates an OKF bundle for a registry" $ do
       withSystemTempDirectory "seihou-okf-docs" $ \tmpDir -> do
@@ -45,12 +73,98 @@
           Left err -> expectationFailure ("Expected walkBundle success, got " <> show err)
           Right concepts ->
             Okf.validateBundle
-              Okf.PermissiveConformance
-              Okf.VersionUndeclared
+              Okf.StrictAuthoring
+              (Okf.VersionDeclared Okf.supportedOkfVersion)
               (Okf.bundleInventoryOfConcepts concepts)
               concepts
               `shouldBe` []
 
+    it "generates complete current declarations without executing commands and regenerates identical bytes" $
+      withSystemTempDirectory "seihou-current-docs" $ \tmpDir -> do
+        let registryDir = tmpDir </> "registry"
+            outDir = tmpDir </> "out"
+        createDirectoryIfMissing True registryDir
+        writeCurrentFixtureRegistry registryDir
+        runDocs (docsOpts registryDir outDir False) `shouldReturn` Right ("Wrote 9 concepts to " <> T.pack outDir)
+        first <- snapshot outDir
+        runDocs (docsOpts registryDir outDir True) `shouldReturn` Right ("Wrote 9 concepts to " <> T.pack outDir)
+        snapshot outDir `shouldReturn` first
+        runDocs (docsOpts registryDir outDir True & #profile ?~ (outDir </> "profile.dhall"))
+          `shouldReturn` Right ("Wrote 9 concepts to " <> T.pack outDir)
+        snapshot outDir `shouldReturn` first
+        doesPathExist (registryDir </> "SHOULD-NOT-EXIST") `shouldReturn` False
+        let page name = TIO.readFile (outDir </> name)
+        app <- page "modules/app.md"
+        mapM_
+          (\text -> app `shouldSatisfy` T.isInfixOf text)
+          [ "Managed:",
+            "Seed: created once",
+            "Checkout: portable symlink",
+            "seihou sync",
+            "repeatable checkout setup",
+            "from `https://example.invalid/modules.git`",
+            "`` exact|`literal` ``",
+            "every matching logical line",
+            "[docs-review](/judgements/docs-review.md)",
+            "when `IsSet project.name`",
+            "from `https://example.invalid/reviews.git`"
+          ]
+        app `shouldNotSatisfy` T.isInfixOf "](/modules/base.md)"
+        recipe <- page "recipes/app-recipe.md"
+        recipe `shouldSatisfy` T.isInfixOf "[base](/modules/base.md)"
+        recipe `shouldNotSatisfy` T.isInfixOf "[app-recipe](/recipes/app-recipe.md)"
+        recipe `shouldSatisfy` T.isInfixOf "inherited declarations are not expanded"
+        recipe `shouldSatisfy` T.isInfixOf "(JSON string)"
+        blueprint <- page "blueprints/app-blueprint.md"
+        blueprint `shouldNotSatisfy` T.isInfixOf "](/modules/base.md)"
+        blueprint `shouldSatisfy` T.isInfixOf "No additional tools declared (explicit empty allowlist)"
+        blueprint `shouldSatisfy` T.isInfixOf "mode: `reserved` (reserved; ignored)"
+        blueprint `shouldSatisfy` T.isInfixOf "````text\nFirst line\n```text\nsecond | line\n````"
+        judgement <- page "judgements/docs-review.md"
+        mapM_
+          (\text -> judgement `shouldSatisfy` T.isInfixOf text)
+          [ "Yes means: `Documentation is stale`",
+            "No means: `Documentation matches`",
+            "Category explanation",
+            "useGate = false (disabled)",
+            "passAt = 0.05, blockAt = 0.9",
+            "maxFileBytes: 262144",
+            "maxChunkBytes: 49152",
+            "maxChunks: 16",
+            "maxAttempts: 3",
+            "timeoutSeconds: 120",
+            "maxFindings: 20",
+            "not calibrated risk estimates"
+          ]
+        validateWritten outDir
+
+    it "preserves every existing output file on invalid input and rejecting profiles even with force" $
+      withSystemTempDirectory "seihou-docs-preservation" $ \tmpDir -> do
+        let registryDir = tmpDir </> "registry"
+            outDir = tmpDir </> "out"
+            profilePath = tmpDir </> "reject.dhall"
+        writeFixtureRegistry registryDir
+        runDocs (docsOpts registryDir outDir False) `shouldReturn` Right ("Wrote 3 concepts to " <> T.pack outDir)
+        originalOutput <- snapshot outDir
+        TIO.writeFile profilePath demandingProfile
+        rejected <- runDocs (docsOpts registryDir outDir True & #profile ?~ profilePath)
+        rejected `shouldSatisfy` either (T.isInfixOf "missing required field stale_after") (const False)
+        snapshot outDir `shouldReturn` originalOutput
+        TIO.writeFile (registryDir </> "modules/base/module.dhall") "{ invalid"
+        invalid <- runDocs (docsOpts registryDir outDir True)
+        invalid `shouldSatisfy` either (T.isInfixOf "failed to load registry entry base") (const False)
+        snapshot outDir `shouldReturn` originalOutput
+
+    it "accepts an older custom descriptor through OKF's compatibility decoder" $
+      withSystemTempDirectory "seihou-old-profile" $ \tmpDir -> do
+        let registryDir = tmpDir </> "registry"
+            outDir = tmpDir </> "out"
+            profilePath = tmpDir </> "legacy.dhall"
+        writeFixtureRegistry registryDir
+        TIO.writeFile profilePath "{name=\"legacy\",okfVersion=\"0.1\",allowUnknownTypes=True,idField=None Text,frontmatter={required=[] : List Text,recommended=[] : List Text},types=[] : List {type : Text,pathPattern : Optional Text,resourceScheme : Optional Text,requireSchemaSection : Bool,schemaColumns : List Text,idPrefix : Optional Text}}"
+        runDocs (docsOpts registryDir outDir False & #profile ?~ profilePath)
+          `shouldReturn` Right ("Wrote 3 concepts to " <> T.pack outDir)
+
     it "refuses to overwrite a non-empty output directory without force" $ do
       withSystemTempDirectory "seihou-okf-docs-force" $ \tmpDir -> do
         let registryDir = tmpDir </> "registry"
@@ -135,6 +249,34 @@
 
 demandingProfileAnchor :: T.Text
 demandingProfileAnchor = "[ scalar \"type\""
+
+snapshot :: FilePath -> IO [(FilePath, T.Text)]
+snapshot root = do
+  names <- sort <$> listDirectory root
+  fmap concat $ forM names $ \name -> do
+    let path = root </> name
+    directory <- doesDirectoryExist path
+    if directory
+      then map (\(relative, bytes) -> (name </> relative, bytes)) <$> snapshot path
+      else do
+        bytes <- TIO.readFile path
+        pure [(name, bytes)]
+
+validateWritten :: FilePath -> IO ()
+validateWritten outDir = do
+  Right concepts <- Okf.walkBundle outDir
+  Okf.validateBundle
+    Okf.StrictAuthoring
+    (Okf.VersionDeclared Okf.supportedOkfVersion)
+    (Okf.bundleInventoryOfConcepts concepts)
+    concepts
+    `shouldBe` []
+  profileSpec <- Dhall.inputFile Dhall.auto (outDir </> "profile.dhall") :: IO Profile.ProfileSpec
+  -- A current descriptor must use the current decoder, not depend on a frozen
+  -- fallback. Its new nested policy/constraint fields survive decoding.
+  profileSpec ^. #okfVersion `shouldBe` "0.2"
+  Right compiled <- pure (Profile.compileProfile profileSpec)
+  Profile.validateProfile Okf.StrictAuthoring compiled concepts `shouldBe` []
 
 writeFixtureRegistry :: FilePath -> IO ()
 writeFixtureRegistry registryDir = do
