diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
 # Changelog
 
+## Unreleased
+
+## 0.4.1.1 — 2026-10-16
+
+### Changed
+
+- Require the compatible Shikumi patch releases built against Baikai 0.8.
+
+- Upgrade `mori://shinzui/baikai/packages/baikai` to `>=0.8.0.0 && <0.9`.
+
+### Fixed
+
+- Correct Haddock markup and identifier links; no runtime or public API changes.
+
 ## 0.4.1.0 — 2026-09-30
 
 - Give `ReAct`'s `ProtocolAuto` its own tool-calling check instead of reusing `Shikumi.Adapter.capabilityFor`. Behavior is unchanged: only `openai` over Chat Completions or Responses and `anthropic` over Messages use provider-native tool calling. CLI models, which `shikumi` 0.4.1.0 now treats as schema-native, still use the prompt tool protocol, because Baikai's CLI providers drop tools. Raise the internal `shikumi` bound to `^>=0.4.1.0` so this check never pairs with an older `shikumi`.
diff --git a/shikumi-tools.cabal b/shikumi-tools.cabal
--- a/shikumi-tools.cabal
+++ b/shikumi-tools.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.4
 name: shikumi-tools
-version: 0.4.1.0
+version: 0.4.1.1
 synopsis:
   Typed tools and ReAct agents for shikumi LM programs (EP-11)
 
@@ -68,7 +68,7 @@
 
   build-depends:
     aeson >=2.2 && <2.3,
-    baikai >=0.7.1.0 && <0.8,
+    baikai >=0.8.0.0 && <0.9,
     base >=4.20 && <5,
     bytestring >=0.11 && <0.13,
     containers >=0.6 && <0.9,
@@ -82,7 +82,7 @@
     lens ^>=5.3,
     process >=1.6 && <1.7,
     regex-tdfa >=1.3 && <1.4,
-    shikumi ^>=0.4.1.0,
+    shikumi ^>=0.4.1.1,
     text ^>=2.1,
     vector >=0.13 && <0.14,
 
@@ -122,7 +122,7 @@
 
   build-depends:
     aeson,
-    baikai >=0.7.1.0 && <0.8,
+    baikai >=0.8.0.0 && <0.9,
     base,
     bytestring,
     containers,
@@ -131,10 +131,10 @@
     filepath,
     generic-lens,
     lens,
-    shikumi ^>=0.4.1.0,
-    shikumi-cache ^>=0.2.0.0,
+    shikumi ^>=0.4.1.1,
+    shikumi-cache ^>=0.2.0.2,
     shikumi-testing ^>=0.1.0.0,
-    shikumi-tools ^>=0.4.0.0,
+    shikumi-tools ^>=0.4.1.1,
     tasty,
     tasty-hunit,
     text,
diff --git a/src/Shikumi/Agent/ReAct.hs b/src/Shikumi/Agent/ReAct.hs
--- a/src/Shikumi/Agent/ReAct.hs
+++ b/src/Shikumi/Agent/ReAct.hs
@@ -8,14 +8,14 @@
 -- finished. The runtime executes the selected tool (via "Shikumi.Tool"), records the
 -- result as an /observation/, and asks the model again — until the model finishes or
 -- a @maxIters@ bound is hit. A final /extract/ step turns the finished
--- 'Trajectory' into the typed output the caller wanted.
+-- t'Trajectory' into the typed output the caller wanted.
 --
 -- The whole loop is wrapped in 'Shikumi.Program.Embed', so @react@ is an ordinary
--- @'Program' i o@: it runs under 'Shikumi.Program.runProgram' (the body needs only
+-- @t'Program' i o@: it runs under 'Shikumi.Program.runProgram' (the body needs only
 -- @LLM@ + @Error ShikumiError@, integration point #4's exact row), is structurally
 -- inspectable, and composes with every other shikumi program and combinator.
 --
--- Two tool /protocols/ are supported behind one internal interface ('ProtocolImpl'),
+-- Two tool /protocols/ are supported behind one internal interface ([ProtocolImpl]("Shikumi.Agent.ReAct#t:ProtocolImpl")),
 -- selected by a 'ToolProtocol' value ('ProtocolAuto' resolves per model from a
 -- tool-calling table of first-party HTTP hosts, kept separate from
 -- "Shikumi.Adapter"'s schema-only 'Shikumi.Adapter.capabilityFor'): a provider-native function-calling path
@@ -125,7 +125,7 @@
 
 -- | One recorded (thought, action, observation) step. @observation@ is 'Nothing'
 -- for 'Finish'; for a tool call it is the tool result text or the rendered
--- 'Shikumi.Tool.ToolError'.
+-- t'Shikumi.Tool.ToolError'.
 data Step = Step
   { thought :: !Text,
     action :: !Action,
@@ -136,7 +136,7 @@
 -- | Why the loop stopped. 'TerminatedBudget' is retained for forward
 -- compatibility; the loop itself produces only 'TerminatedFinish' and
 -- 'TerminatedMaxIters' (the budget ceiling is enforced one layer down by the
--- resilient @LLM@ interpreter, surfacing as a 'ShikumiError').
+-- resilient @LLM@ interpreter, surfacing as a t'ShikumiError').
 data Termination
   = TerminatedFinish
   | TerminatedMaxIters !Int
@@ -182,7 +182,7 @@
 -- Building agents
 -- ---------------------------------------------------------------------------
 
--- | Build a ReAct agent as a @'Program' i o@ that returns the typed answer and
+-- | Build a ReAct agent as a @t'Program' i o@ that returns the typed answer and
 -- drops the trajectory. The ergonomic default.
 react ::
   forall i o.
@@ -193,7 +193,7 @@
   Program i o
 react sig reg cfg = FMap fst (reactWithTrajectory sig reg cfg)
 
--- | Build a ReAct agent that also returns the recorded 'Trajectory', for
+-- | Build a ReAct agent that also returns the recorded t'Trajectory', for
 -- evaluators/optimizers and tests that assert on the steps. When compaction ran,
 -- the trajectory contains 'Summarized' steps whose observations carry the summary.
 reactWithTrajectory ::
@@ -205,7 +205,7 @@
   Program i (o, Trajectory)
 reactWithTrajectory sig reg cfg = embed (reactLoop sig reg cfg)
 
--- | The agent loop, embedded into a 'Program' by 'reactWithTrajectory'. Runs in
+-- | The agent loop, embedded into a t'Program' by 'reactWithTrajectory'. Runs in
 -- exactly 'Shikumi.Program.runProgram'\'s effect row.
 reactLoop ::
   forall i o es.
@@ -242,7 +242,7 @@
               loop (iter + 1) acc''
 
     -- The final extract call: render, issue, decode into @o@ (a decode failure
-    -- here is the agent's final-answer failure, surfaced as a 'ShikumiError').
+    -- here is the agent's final-answer failure, surfaced as a t'ShikumiError').
     extract :: Trajectory -> Eff es (o, Trajectory)
     extract traj = do
       let (ctx, opts) = renderExtract impl i traj
@@ -380,7 +380,7 @@
              ("anthropic", B.AnthropicMessages)
            ]
 
--- | Build the concrete 'ProtocolImpl' for a model, choosing the native or prompt
+-- | Build the concrete t'ProtocolImpl' for a model, choosing the native or prompt
 -- renderers from 'resolveProtocolKind'.
 resolveProtocol ::
   forall i o.
@@ -584,7 +584,7 @@
 actionToProposal (_, Summarized) =
   error "internal invariant violated: Summarized is never parsed from model output"
 
--- | Strip a leading/trailing Markdown code fence (```… / ```json … ```), if any,
+-- | Strip a leading\/trailing Markdown code fence (```… \/ ```json … ```), if any,
 -- so a fenced JSON reply still decodes. Falls back to the input unchanged.
 stripFences :: Text -> Text
 stripFences t =
diff --git a/src/Shikumi/CodeExec/CodeAct.hs b/src/Shikumi/CodeExec/CodeAct.hs
--- a/src/Shikumi/CodeExec/CodeAct.hs
+++ b/src/Shikumi/CodeExec/CodeAct.hs
@@ -2,9 +2,9 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 
 -- | @codeAct@ (EP-27): a multi-turn loop where each /action/ is a code snippet that
--- may call provided tools, accumulating a 'Trajectory', then extracting the typed
+-- may call provided tools, accumulating a t'Trajectory', then extracting the typed
 -- answer. It mirrors DSPy's @CodeAct@ (a ReAct loop whose action is code) and reuses
--- 'Shikumi.Agent.ReAct'\'s @Trajectory@/@Step@/@Termination@ data model.
+-- "Shikumi.Agent.ReAct"\'s @Trajectory@\/@Step@\/@Termination@ data model.
 --
 -- The hermetic restricted interpreter does not itself call host functions, so tool
 -- use is handled at the /protocol/ level: a snippet of the exact form
@@ -74,7 +74,7 @@
   Program i o
 codeAct sig reg = FMap fst (codeActWithTrajectory defaultCodeActConfig sig reg)
 
--- | Build a @codeAct@ agent that also returns the recorded 'Trajectory'.
+-- | Build a @codeAct@ agent that also returns the recorded t'Trajectory'.
 codeActWithTrajectory ::
   (ToPrompt i, ToSchema o, FromModel o, Validatable o) =>
   CodeActConfig ->
@@ -184,7 +184,7 @@
     turnSys = getInstruction sig <> "\n\n" <> toolMenu reg <> "\n\n" <> codeActGuide
     extractSys = getInstruction sig <> "\n\n" <> schemaInstruction (toSchema (Proxy @o))
 
--- | Parse a model turn @{"code": "...", "finished": <bool>}@ (fences stripped).
+-- | Parse a model turn @{"code": "...", "finished": \<bool\>}@ (fences stripped).
 parseCodeReply :: Text -> Either Text (Text, Bool)
 parseCodeReply raw = case eitherDecodeStrict (encodeUtf8 (stripFences raw)) of
   Left e -> Left ("not valid JSON: " <> T.pack e)
@@ -199,7 +199,7 @@
     pure (code, fin)
   Right _ -> Left "expected a JSON object"
 
--- | Recognize a tool-call snippet @call("name", <argsJSON>)@, decoding the name and
+-- | Recognize a tool-call snippet @call("name", \<argsJSON\>)@, decoding the name and
 -- arguments. Anything else returns 'Nothing' (it is handed to the sandbox).
 parseCall :: Text -> Maybe (Text, Value)
 parseCall code = do
diff --git a/src/Shikumi/CodeExec/Interpreter.hs b/src/Shikumi/CodeExec/Interpreter.hs
--- a/src/Shikumi/CodeExec/Interpreter.hs
+++ b/src/Shikumi/CodeExec/Interpreter.hs
@@ -2,10 +2,10 @@
 
 -- | The sandbox as a swappable /value/ (EP-27).
 --
--- A 'CodeInterpreter' takes a string of model-emitted code and returns either a
+-- A t'CodeInterpreter' takes a string of model-emitted code and returns either a
 -- recoverable error message (fed back to the model) or the code's textual output.
 -- It is captured in an 'Shikumi.Program.Embed' closure exactly as a
--- 'Shikumi.Tool.ToolRegistry' is — never added to the effect row — because the
+-- t'Shikumi.Tool.ToolRegistry' is — never added to the effect row — because the
 -- @Embed@ body is fixed to the row @(LLM, Error ShikumiError)@. The hermetic
 -- interpreters here are /pure/ (they ignore the effects and 'pure' their result), so
 -- they fit inside any row.
@@ -17,12 +17,12 @@
 -- and evaluates it purely — there is no syscall it can make, no filesystem, no
 -- network, no unbounded loop primitive (a step/expression-size cap bounds runtime and
 -- returns a typed error past it). The dangerous path is a real subprocess
--- interpreter (DSPy uses a @deno@/Pyodide sandbox); that is intentionally /not/
+-- interpreter (DSPy uses a @deno@\/Pyodide sandbox); that is intentionally /not/
 -- shipped here. A future, gated, non-CI @subprocessInterpreter@ (EP-27 M4) would have
 -- to enforce, at minimum: no network; no host filesystem access beyond a single fresh
--- scratch dir; no environment inheritance; CPU/memory/wall-clock limits (killing the
+-- scratch dir; no environment inheritance; CPU\/memory\/wall-clock limits (killing the
 -- child and surfacing a 'Shikumi.Error.Timeout'); and loud failure (any limit breach
--- becomes a 'Shikumi.Error.ShikumiError', never a silent empty result). The effect
+-- becomes a t'Shikumi.Error.ShikumiError', never a silent empty result). The effect
 -- row does not type-enforce purity: the built-in tool environments already perform
 -- real IO inside the same row via 'Shikumi.Tool.Env.toolIO' and
 -- 'Shikumi.Tool.Web.httpIO'. The hermeticity of the shipped interpreters is a
@@ -34,7 +34,7 @@
 --
 -- A single expression (optionally prefixed @result = @), built from: integer and
 -- rational literals, unary minus, and @+ - * /@ with parentheses; string literals
--- with @\"@, @\\@, @\n@, and @\t@ escapes plus @++@ concatenation and the functions
+-- with @\\"@, @\\\\@, @\\n@, and @\\t@ escapes plus @++@ concatenation and the functions
 -- @len@, @upper@, @lower@; and list literals @[a, b, …]@ with @sum@, @length@,
 -- @concat@. The value of the expression is rendered to text as the output. A parse
 -- error, an unknown identifier/function, a type error, division by zero, or
@@ -66,7 +66,7 @@
 -- | A sandbox as a plain value: run a code string, get back either a recoverable
 -- error message (@Left@, fed to the model) or the program's textual output
 -- (@Right@). A genuine infrastructure failure (e.g. a real subprocess dying) would
--- be thrown as a 'ShikumiError' from inside 'runCode'; the hermetic interpreters
+-- be thrown as a t'ShikumiError' from inside 'runCode'; the hermetic interpreters
 -- never do that.
 newtype CodeInterpreter = CodeInterpreter
   { runCode :: forall es. (LLM :> es, Error ShikumiError :> es) => Text -> Eff es (Either Text Text)
diff --git a/src/Shikumi/CodeExec/ProgramOfThought.hs b/src/Shikumi/CodeExec/ProgramOfThought.hs
--- a/src/Shikumi/CodeExec/ProgramOfThought.hs
+++ b/src/Shikumi/CodeExec/ProgramOfThought.hs
@@ -6,11 +6,11 @@
 -- a typed answer.
 --
 -- The loop mirrors DSPy's @ProgramOfThought.forward@: ask the model for a code
--- snippet that computes the answer; run it through the captured 'CodeInterpreter';
+-- snippet that computes the answer; run it through the captured t'CodeInterpreter';
 -- if it errors, feed the error back and ask for a correction (up to @maxIters@
 -- attempts); once it runs, ask the model to extract the typed output @o@ from the
 -- code and its output. The whole loop is a single 'Shikumi.Program.Embed' node, so
--- @programOfThought@ is an ordinary @'Program' i o@ that carries no tunable
+-- @programOfThought@ is an ordinary @t'Program' i o@ that carries no tunable
 -- parameters (the parameter-count invariant holds: @foldParams (programOfThought
 -- sig) == []@) and composes/serializes exactly like @react@.
 module Shikumi.CodeExec.ProgramOfThought
@@ -56,7 +56,7 @@
 programOfThought = programOfThoughtWith defaultPoTConfig
 
 -- | @programOfThought@ with an explicit config (e.g. a different @maxIters@ or a
--- different 'CodeInterpreter').
+-- different t'CodeInterpreter').
 programOfThoughtWith ::
   (ToPrompt i, ToSchema o, FromModel o, Validatable o) =>
   PoTConfig ->
diff --git a/src/Shikumi/CodeExec/Prompt.hs b/src/Shikumi/CodeExec/Prompt.hs
--- a/src/Shikumi/CodeExec/Prompt.hs
+++ b/src/Shikumi/CodeExec/Prompt.hs
@@ -1,6 +1,6 @@
 -- | Small prompt/parse helpers shared by the code-execution modules
--- ('Shikumi.CodeExec.ProgramOfThought', 'Shikumi.CodeExec.CodeAct'). These mirror
--- the equivalents in 'Shikumi.Agent.ReAct' (which keeps its own copies private);
+-- ("Shikumi.CodeExec.ProgramOfThought", "Shikumi.CodeExec.CodeAct"). These mirror
+-- the equivalents in "Shikumi.Agent.ReAct" (which keeps its own copies private);
 -- they are factored here to avoid duplicating the few small helpers across the two
 -- code-execution modules (EP-27).
 module Shikumi.CodeExec.Prompt
@@ -29,9 +29,9 @@
     emptyOptions
   )
 
--- | Strip a leading/trailing Markdown code fence (@```@ / @```json … ```@), if any,
+-- | Strip a leading\/trailing Markdown code fence (@```@ \/ @```json … ```@), if any,
 -- so a fenced reply still decodes. Falls back to the trimmed input. (Same logic as
--- 'Shikumi.Agent.ReAct'\'s private @stripFences@.)
+-- "Shikumi.Agent.ReAct"\'s private @stripFences@.)
 stripFences :: Text -> Text
 stripFences t =
   let trimmed = T.strip t
diff --git a/src/Shikumi/Tool.hs b/src/Shikumi/Tool.hs
--- a/src/Shikumi/Tool.hs
+++ b/src/Shikumi/Tool.hs
@@ -5,19 +5,19 @@
 -- | Typed tools and their lowering to baikai's wire tool — the MasterPlan's
 -- integration point #8, owned here and nowhere else.
 --
--- A @'Tool' i o@ is a named function from an input record @i@ to an output @o@,
+-- A @t'Tool' i o@ is a named function from an input record @i@ to an output @o@,
 -- runnable in any effect stack that has the @LLM@ and @Error ShikumiError@
 -- capabilities (so a tool body may itself call sub-models and may signal failure
 -- through the typed error channel — see the plan's Decision Log on why the row is
 -- @(LLM, Error ShikumiError)@ rather than @IOE@). From a tool shikumi derives the
--- JSON Schema of @i@ (reusing 'Shikumi.Schema'), lowers it to baikai's untyped
+-- JSON Schema of @i@ (reusing "Shikumi.Schema"), lowers it to baikai's untyped
 -- @Baikai.Tool { name, description, parameters }@, decodes the model's argument
--- JSON into a typed @i@ (or a 'ToolError', never a crash), runs the body, and
+-- JSON into a typed @i@ (or a t'ToolError', never a crash), runs the body, and
 -- encodes the @o@ result back to observation text.
 --
--- Heterogeneity is handled by the 'SomeTool' existential: it hides @i@/@o@ while
--- retaining, at wrap time, the schema/decode/encode dictionaries the wire boundary
--- needs. A 'ToolRegistry' is a name-keyed map of 'SomeTool's that a ReAct loop
+-- Heterogeneity is handled by the t'SomeTool' existential: it hides @i@/@o@ while
+-- retaining, at wrap time, the schema\/decode\/encode dictionaries the wire boundary
+-- needs. A t'ToolRegistry' is a name-keyed map of t'SomeTool's that a ReAct loop
 -- dispatches against by the tool name the model emits at run time.
 module Shikumi.Tool
   ( -- * Typed tools
@@ -87,8 +87,8 @@
   }
 
 -- | Build a tool from a name, a description, and an effectful body. A pure tool is
--- just @\\i -> pure (f i)@. The schema/decode/encode dictionaries are not needed
--- here — they are captured later, at 'SomeTool' wrap time.
+-- just @\\i -> pure (f i)@. The schema\/decode\/encode dictionaries are not needed
+-- here — they are captured later, at t'SomeTool' wrap time.
 mkTool ::
   Text ->
   Text ->
@@ -159,7 +159,7 @@
 
 -- | Run an erased tool against a raw JSON arguments object: decode to the hidden
 -- @i@, run the body, encode the @o@ to text. A decode failure becomes
--- 'ToolArgsInvalid', a body that throws a recoverable 'ShikumiError' becomes
+-- 'ToolArgsInvalid', a body that throws a recoverable t'ShikumiError' becomes
 -- 'ToolRunFailed', and a body that throws an infrastructure error
 -- ('isInfraToolError') rethrows to abort the caller's loop.
 runErased ::
@@ -216,17 +216,17 @@
 -- | A tool-call failure carried as a /value/. The agent feeds the rendered text
 -- back to the model as an observation so it can recover, and records it in the
 -- trajectory; infrastructure faults ('isInfraToolError': budget and
--- context-window exhaustion) bubble up as a 'ShikumiError' and abort the loop.
+-- context-window exhaustion) bubble up as a t'ShikumiError' and abort the loop.
 data ToolError
   = -- | the model named a tool the registry does not have
     ToolNotFound !Text
   | -- | tool name, decode-error message: the arguments did not match @i@
     ToolArgsInvalid !Text !Text
-  | -- | tool name, failure message: the body signalled a 'ShikumiError'
+  | -- | tool name, failure message: the body signalled a t'ShikumiError'
     ToolRunFailed !Text !Text
   deriving stock (Show, Eq)
 
--- | Render a 'ToolError' as the observation text fed back to the model.
+-- | Render a t'ToolError' as the observation text fed back to the model.
 renderToolError :: ToolError -> Text
 renderToolError = \case
   ToolNotFound nm -> "Error: no such tool \"" <> nm <> "\"."
@@ -262,7 +262,7 @@
 shikumiErrorText :: ShikumiError -> Text
 shikumiErrorText = renderShikumiError
 
--- | Which 'ShikumiError's must escape the agent loop rather than become
+-- | Which t'ShikumiError's must escape the agent loop rather than become
 -- observations. Budget and context-window exhaustion are infrastructure faults:
 -- the model cannot recover from them by reading an observation, and continuing
 -- the loop would either overspend (budget) or deterministically re-fail
diff --git a/src/Shikumi/Tool/Builtin/Fs.hs b/src/Shikumi/Tool/Builtin/Fs.hs
--- a/src/Shikumi/Tool/Builtin/Fs.hs
+++ b/src/Shikumi/Tool/Builtin/Fs.hs
@@ -1,8 +1,8 @@
 {-# LANGUAGE RankNTypes #-}
 
--- | Built-in filesystem tools over 'ToolEnv'.
+-- | Built-in filesystem tools over t'ToolEnv'.
 --
--- The search tools prefer host-provided @rg@/@fd@ through 'ToolEnv.exec' when
+-- The search tools prefer host-provided @rg@/@fd@ through 'Shikumi.Tool.Env.envExec' when
 -- available, and fall back to a bounded in-process traversal through the same
 -- environment record. The fallback skips noisy directories, binary files, and
 -- large files, and caps traversal depth and result count.
diff --git a/src/Shikumi/Tool/Builtin/Shell.hs b/src/Shikumi/Tool/Builtin/Shell.hs
--- a/src/Shikumi/Tool/Builtin/Shell.hs
+++ b/src/Shikumi/Tool/Builtin/Shell.hs
@@ -1,10 +1,10 @@
--- | Built-in shell tool over 'ToolEnv.exec'.
+-- | Built-in shell tool over 'Shikumi.Tool.Env.envExec'.
 --
 -- Security posture: 'bashTool' executes arbitrary model-chosen shell commands
--- through the supplied 'ToolEnv', with whatever filesystem access, environment,
--- and process privileges that environment grants. With 'localToolEnv' this is
+-- through the supplied t'ToolEnv', with whatever filesystem access, environment,
+-- and process privileges that environment grants. With 'Shikumi.Tool.Env.localToolEnv' this is
 -- deliberately non-hermetic host execution for trusted, local, single-operator
--- workflows. Sandboxing is achieved by supplying a confining 'ToolEnv', not by
+-- workflows. Sandboxing is achieved by supplying a confining t'ToolEnv', not by
 -- this module.
 module Shikumi.Tool.Builtin.Shell
   ( BashReq (..),
diff --git a/src/Shikumi/Tool/Env.hs b/src/Shikumi/Tool/Env.hs
--- a/src/Shikumi/Tool/Env.hs
+++ b/src/Shikumi/Tool/Env.hs
@@ -5,13 +5,13 @@
 --
 -- Filesystem and process-backed tools should depend on this record instead of
 -- calling the host operating system directly. A future sandbox can provide a
--- different 'ToolEnv' value while the tool definitions stay unchanged.
+-- different t'ToolEnv' value while the tool definitions stay unchanged.
 --
 -- Security posture: 'localToolEnv' is deliberately non-hermetic. It has full
 -- access to the host filesystem paths and inherited environment visible to the
 -- current process, and it can execute arbitrary shell commands with the current
 -- user's privileges. Its filesystem and process IO is performed inside the
--- @(LLM, Error ShikumiError)@ row via 'unsafeEff_'. Supply a different 'ToolEnv'
+-- @(LLM, Error ShikumiError)@ row via 'unsafeEff_'. Supply a different t'ToolEnv'
 -- to confine tools; this module is the sandboxing seam, not the sandbox.
 module Shikumi.Tool.Env
   ( Path,
diff --git a/src/Shikumi/Tool/Web.hs b/src/Shikumi/Tool/Web.hs
--- a/src/Shikumi/Tool/Web.hs
+++ b/src/Shikumi/Tool/Web.hs
@@ -3,14 +3,14 @@
 
 -- | Swappable HTTP operations for the built-in web tools.
 --
--- 'web_fetch' is functional with only a TLS 'Manager'. 'web_search' requires a
+-- @web_fetch@ is functional with only a TLS 'Manager'. @web_search@ requires a
 -- configured provider because search APIs need provider-specific credentials.
 --
 -- Security posture: the default fetch policy denies non-http(s) schemes and the
 -- most common SSRF destinations (loopback, link-local, and RFC-1918 private host
 -- literals) before opening a connection, and caps response bytes while reading.
 -- It does not resolve hostnames to detect private addresses behind DNS; supply a
--- stricter 'FetchPolicy' through 'localWebClientWith' when deployments need that.
+-- stricter t'FetchPolicy' through 'localWebClientWith' when deployments need that.
 module Shikumi.Tool.Web
   ( FetchResult (..),
     SearchHit (..),
diff --git a/test/Fixtures.hs b/test/Fixtures.hs
--- a/test/Fixtures.hs
+++ b/test/Fixtures.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DeriveAnyClass #-}
 
 -- | Shared fixtures for the @shikumi-tools@ specs: the weather records from the
--- plan's Purpose, their schema/decode/prompt instances, the typed @weatherTool@ and
+-- plan's Purpose, their schema\/decode\/prompt instances, the typed @weatherTool@ and
 -- its registry, the mock-LM scripts for both protocols, and the expected schema and
 -- answer the specs assert against.
 module Fixtures
diff --git a/test/RestrictedSpec.hs b/test/RestrictedSpec.hs
--- a/test/RestrictedSpec.hs
+++ b/test/RestrictedSpec.hs
@@ -1,5 +1,5 @@
 -- | EP-27 M1: the hermetic restricted-DSL evaluator behind 'restrictedInterpreter'.
--- Pure unit tests over 'runRestricted': it computes arithmetic/string/list results
+-- Pure unit tests over 'runRestricted': it computes arithmetic\/string\/list results
 -- and returns a @Left@ error message for division by zero, a parse error, or an
 -- unknown function — the recoverable errors the @programOfThought@ loop feeds back.
 module RestrictedSpec (tests) where
