Liking cljdoc? Tell your friends :D

skeptic.analysis

No vars found in this namespace.

skeptic.analysis.annotate.cljs

ClojureScript-only AST op handlers. Mirrors skeptic.analysis.annotate.jvm: each handler recurses children via the step trampoline and assigns a :type derived from the cljs :tag (translated via av/cljs-tag->type), falling back to Dyn when the tag is any or unrecognized.

ClojureScript-only AST op handlers. Mirrors `skeptic.analysis.annotate.jvm`:
each handler recurses children via the step trampoline and assigns a `:type`
derived from the cljs `:tag` (translated via `av/cljs-tag->type`), falling
back to `Dyn` when the tag is `any` or unrecognized.
raw docstring

skeptic.analysis.annotate.prune

Recursive AST-node prune. Drops slots that no skeptic reader consumes and that would otherwise root very large per-node payloads through the locals env and analyzer-state references on deep cljs ASTs.

Removed: :env everywhere; :info everywhere except cljs :var ops, where it is reduced to {:name ... :meta ...} (the call-symbol path at annotate/api.clj:42 plus the Var-metadata path at schema/collect/cljs.clj:94 / malli_spec/collect/cljs.clj:57). Recurses through the analyzer's :children slot the same way as the cljs intake walker; AST shape is otherwise preserved so every existing reader of :init / :binding-init / :fn-binding-node / :meta continues to see the fields it expects.

Recursive AST-node prune. Drops slots that no skeptic reader consumes
and that would otherwise root very large per-node payloads through the
locals env and analyzer-state references on deep cljs ASTs.

Removed: `:env` everywhere; `:info` everywhere except cljs `:var` ops,
where it is reduced to `{:name ... :meta ...}` (the call-symbol path at
`annotate/api.clj:42` plus the Var-metadata path at
`schema/collect/cljs.clj:94` / `malli_spec/collect/cljs.clj:57`).
Recurses through the analyzer's `:children`
slot the same way as the cljs intake walker; AST shape is otherwise
preserved so every existing reader of `:init` / `:binding-init` /
`:fn-binding-node` / `:meta` continues to see the fields it expects.
raw docstring

skeptic.analysis.annotate.runner

Trampoline for the annotate helpers.

End state (Phase 7): every annotate helper has signature (Ctx, AnnotatedNode) -> Step, where Step is the sum type below.

[:done annotated-node]
[:call helper-fn ctx node k]

run is a loop over a heap-allocated stack of continuations. During the Phase 2-6 migration window, some helpers still return plain AnnotatedNode values; step? / normalize plus the [:done v] auto-wrap branch inside run carry those through. All three are migration-window cruft and are deleted in Phase 7 contraction.

Trampoline for the annotate helpers.

End state (Phase 7): every annotate helper has signature
`(Ctx, AnnotatedNode) -> Step`, where `Step` is the sum type below.

    [:done annotated-node]
    [:call helper-fn ctx node k]

`run` is a loop over a heap-allocated stack of continuations. During
the Phase 2-6 migration window, some helpers still return plain
AnnotatedNode values; `step?` / `normalize` plus the [:done v]
auto-wrap branch inside `run` carry those through. All three are
migration-window cruft and are deleted in Phase 7 contraction.
raw docstring

skeptic.analysis.ast-children

Shared tools.analyzer child listing and preorder traversal. Delegates to clojure.tools.analyzer.ast so all callers stay aligned.

Shared tools.analyzer child listing and preorder traversal.
Delegates to clojure.tools.analyzer.ast so all callers stay aligned.
raw docstring

skeptic.analysis.bridge.descriptors

Per-form descriptor extraction for Plumatic source forms stored in the project-wide form-refs map, keyed by qualified symbol (built once in skeptic.checking.pipeline/project-state, threaded through bridge ctx). Pipeline stores prepared descriptors, not raw forms. Each descriptor carries the exact source namespace, alias map, and known declaration qsyms used to resolve schema-reference symbols deterministically.

Descriptor shapes: :defn → {:kind :defn :output-form form :arglists {k {:input-forms [...]}}} :def → {:kind :def :schema-form form} :defschema → {:kind :defschema :schema-form form} :schema-source → {:kind :schema-source :schema-form form}

Prepared descriptors add: :source-env → {:ns ns-sym :aliases {alias target-ns} :ref-qsyms #{qsym}}

Per-form descriptor extraction for Plumatic source forms stored in the
project-wide form-refs map, keyed by qualified symbol (built once in
`skeptic.checking.pipeline/project-state`, threaded through bridge ctx).
Pipeline stores prepared descriptors, not raw forms. Each descriptor carries
the exact source namespace, alias map, and known declaration qsyms used to
resolve schema-reference symbols deterministically.

Descriptor shapes:
  :defn      → {:kind :defn :output-form form :arglists {k {:input-forms [...]}}}
  :def       → {:kind :def :schema-form form}
  :defschema → {:kind :defschema :schema-form form}
  :schema-source → {:kind :schema-source :schema-form form}

Prepared descriptors add:
  :source-env → {:ns ns-sym :aliases {alias target-ns} :ref-qsyms #{qsym}}
raw docstring

skeptic.analysis.class-oracle

Host-side wrappers around the worker's class oracle. Every :class slot on the host carries an opaque handle: an integer for bootstrap host-runtime classes (interned at connect-time via intern-host-classes!), or a UUID string for project classes (minted by the worker on resolve-class-sym). Host NEVER calls Class/forName, .isAssignableFrom, instance?, or class-equality on a project class. Every relation routes through the worker via class-rel.

Host-side wrappers around the worker's class oracle. Every `:class` slot on
the host carries an opaque handle: an integer for bootstrap host-runtime
classes (interned at connect-time via `intern-host-classes!`), or a UUID
string for project classes (minted by the worker on `resolve-class-sym`).
Host NEVER calls `Class/forName`, `.isAssignableFrom`, `instance?`, or
class-equality on a project class. Every relation routes through the
worker via `class-rel`.
raw docstring

skeptic.analysis.native-fns

Native typing for clojure.lang.Numbers static calls and selected clojure.core invokes. Toolchain reference (Clojure 1.11.1, tools.analyzer.jvm 1.2.3):

  • Numbers: inc/dec argc 1; add/multiply argc 2 (n-ary +/* is nested static calls); minus argc 1 or 2; isPos/isNeg argc 1 -> bool.
  • Invoke (native-fn-dict): (+) (+ x) () ( x); str; format; even? odd?; inc when not inlined.
  • Literal (+ 1 2 3) never yields top-level :invoke; test 3-arg + with ((resolve '+) 1 2 3).
Native typing for clojure.lang.Numbers static calls and selected clojure.core invokes.
Toolchain reference (Clojure 1.11.1, tools.analyzer.jvm 1.2.3):
- Numbers: inc/dec argc 1; add/multiply argc 2 (n-ary +/* is nested static calls);
  minus argc 1 or 2; isPos/isNeg argc 1 -> bool.
- Invoke (native-fn-dict): (+) (+ x) (*) (* x); str; format; even? odd?; inc when not inlined.
- Literal (+ 1 2 3) never yields top-level :invoke; test 3-arg + with ((resolve '+) 1 2 3).
raw docstring

skeptic.analysis.predicates

Shared registry of clojure.core predicates that Skeptic can type. Two consumers:

  • native_fns: each predicate is typed Dyn -> Bool when invoked
  • bridge admission: (s/pred f) and Malli's bare-predicate-as-schema both convert to the predicate's witness type.
Shared registry of clojure.core predicates that Skeptic can type.
Two consumers:
  - native_fns: each predicate is typed Dyn -> Bool when invoked
  - bridge admission: (s/pred f) and Malli's bare-predicate-as-schema
    both convert to the predicate's witness type.
raw docstring

skeptic.analysis.types

skeptic.classloader-fix

JDK 9+/Leiningen-bootclasspath workaround for clojure.instant.

Leiningen's launcher appends leiningen-<v>-standalone.jar to the JVM's -Xbootclasspath/a: (unless LEIN_USE_BOOTCLASSPATH=no is set in the environment — which we cannot require of end users). Clojure ends up loaded by the JVM bootstrap classloader. When any code transitively requires clojure.instant, its AOT-compiled <clinit> pushes clojure.lang.Compiler/LOADER = (.getClassLoader thisLoadingFn) = null (bootstrap). RT.classForNameNonLoading("java.sql.Timestamp") then calls Class.forName("java.sql.Timestamp", false, null) against the bootstrap loader. On JDK 9+, java.sql is defined to PlatformClassLoader, NOT bootstrap, so the call raises ClassNotFoundException. The four sibling java.util.* imports just before it succeed because java.util lives in java.base, which IS in bootstrap.

Skeptic transitively pulls clojure.instant via cljs.analyzer.api → cljs.tagged-literals → cljs.instant → clojure.instant, so this triggers at plugin-load time and blocks the skeptic task from running at all.

Fix: clojure.lang.RT/classForName bytecode short-circuits to DynamicClassLoader/findInMemoryClass BEFORE invoking Class.forName(name, false, null) when the passed loader is not itself a DynamicClassLoader. That short-circuit reads DynamicClassLoader/classCache, a static ConcurrentHashMap normally used by Clojure's in-memory class generation. We pre-populate it with "java.sql.Timestamp" → java.sql.Timestamp.class, then eagerly (require 'clojure.instant). Its <clinit> runs immediately, hits our cache entry through the short-circuit, and never falls through to the broken Class.forName(name, false, null) path. The namespace is registered in *loaded-libs*, so the later transitive require from cljs.instant is a no-op and the unsafe <clinit> does not re-run.

This namespace is intentionally required FIRST by skeptic.cljs.analyzer-driver — the earliest namespace on the cljs intake chain — so the cache is populated before any cljs require can drag clojure.instant in.

JDK 9+/Leiningen-bootclasspath workaround for `clojure.instant`.

Leiningen's launcher appends `leiningen-<v>-standalone.jar` to the JVM's
`-Xbootclasspath/a:` (unless LEIN_USE_BOOTCLASSPATH=no is set in the
environment — which we cannot require of end users). Clojure ends up
loaded by the JVM bootstrap classloader. When any code transitively
requires `clojure.instant`, its AOT-compiled `<clinit>` pushes
`clojure.lang.Compiler/LOADER = (.getClassLoader thisLoadingFn) = null`
(bootstrap). `RT.classForNameNonLoading("java.sql.Timestamp")` then
calls `Class.forName("java.sql.Timestamp", false, null)` against the
bootstrap loader. On JDK 9+, `java.sql` is defined to
`PlatformClassLoader`, NOT bootstrap, so the call raises
`ClassNotFoundException`. The four sibling `java.util.*` imports just
before it succeed because `java.util` lives in `java.base`, which IS in
bootstrap.

Skeptic transitively pulls clojure.instant via cljs.analyzer.api →
cljs.tagged-literals → cljs.instant → clojure.instant, so this triggers
at plugin-load time and blocks the `skeptic` task from running at all.

Fix: `clojure.lang.RT/classForName` bytecode short-circuits to
`DynamicClassLoader/findInMemoryClass` BEFORE invoking
`Class.forName(name, false, null)` when the passed loader is not itself a
`DynamicClassLoader`. That short-circuit reads
`DynamicClassLoader/classCache`, a static `ConcurrentHashMap` normally
used by Clojure's in-memory class generation. We pre-populate it with
`"java.sql.Timestamp" → java.sql.Timestamp.class`, then eagerly
`(require 'clojure.instant)`. Its `<clinit>` runs immediately, hits our
cache entry through the short-circuit, and never falls through to the
broken `Class.forName(name, false, null)` path. The namespace is
registered in `*loaded-libs*`, so the later transitive require from
cljs.instant is a no-op and the unsafe `<clinit>` does not re-run.

This namespace is intentionally required FIRST by
`skeptic.cljs.analyzer-driver` — the earliest namespace on the cljs
intake chain — so the cache is populated before any cljs require can
drag clojure.instant in.
raw docstring

No vars found in this namespace.

skeptic.cli.cljs.deps

Source discovery for cljs/cljc files in a deps.edn project. Reuses skeptic.cli.paths/discover-paths to obtain the project's resolved source paths from clojure.tools.deps/create-basis, then walks them for cljs/cljc files. discover-paths returns paths verbatim from the deps.edn (relative strings like "src"); they are absolutized against root here before walking.

Source discovery for cljs/cljc files in a deps.edn project. Reuses
`skeptic.cli.paths/discover-paths` to obtain the project's resolved
source paths from `clojure.tools.deps/create-basis`, then walks them
for cljs/cljc files. `discover-paths` returns paths verbatim from the
deps.edn (relative strings like "src"); they are absolutized against
`root` here before walking.
raw docstring

skeptic.cli.cljs.discover

Walk a collection of source roots and partition the matching files into cljs and cljc buckets. Paths that do not exist are skipped silently; paths that point at a regular file are included if their extension matches.

Walk a collection of source roots and partition the matching files into
cljs and cljc buckets. Paths that do not exist are skipped silently;
paths that point at a regular file are included if their extension matches.
raw docstring

skeptic.cli.cljs.lein

Source discovery for cljs/cljc files in a Leiningen project. Reads :source-paths and :test-paths from the project map, plus any cljsbuild build :source-paths if present, and walks them for cljs/cljc files.

Source discovery for cljs/cljc files in a Leiningen project. Reads
:source-paths and :test-paths from the project map, plus any cljsbuild
build :source-paths if present, and walks them for cljs/cljc files.
raw docstring

skeptic.cli.cljs.shadow

Source discovery for cljs/cljc files in a shadow-cljs project. Reads shadow-cljs.edn at root as plain EDN and walks the top-level :source-paths key for cljs/cljc files.

Source discovery for cljs/cljc files in a shadow-cljs project. Reads
shadow-cljs.edn at `root` as plain EDN and walks the top-level
:source-paths key for cljs/cljc files.
raw docstring

skeptic.cli.main

Legacy deps.edn-side entrypoint for Skeptic.

Hermetic Clojure CLI execution is exposed through skeptic.tool/check and clj -T:skeptic check. clojure -M:skeptic starts from the client project's runtime classpath, so it is intentionally unsupported.

Legacy deps.edn-side entrypoint for Skeptic.

Hermetic Clojure CLI execution is exposed through `skeptic.tool/check`
and `clj -T:skeptic check`. `clojure -M:skeptic` starts from the client
project's runtime classpath, so it is intentionally unsupported.
raw docstring

skeptic.cli.options

Shared CLI option vector and parser for the Leiningen plugin (leiningen.skeptic) and the legacy deps.edn -M entrypoint.

Hermetic deps.edn execution uses the Clojure CLI tool API (skeptic.tool/check) with an EDN arg map instead of argv parsing. Keys produced here land directly in the opts map consumed by skeptic.core/check-project.

Shared CLI option vector and parser for the Leiningen plugin
(leiningen.skeptic) and the legacy deps.edn -M entrypoint.

Hermetic deps.edn execution uses the Clojure CLI tool API
(`skeptic.tool/check`) with an EDN arg map instead of argv parsing.
Keys produced here land directly in the opts map consumed by
skeptic.core/check-project.
raw docstring

skeptic.cli.paths

Source-path discovery for the deps.edn entrypoint. Reads the project's deps.edn through the official tools.deps API and returns the merged :paths vector for the given alias selection. The Leiningen plugin does not use this; it gets paths from the lein project map.

clojure.tools.deps is resolved lazily inside create-basis rather than required at namespace load. That keeps its heavyweight transitive graph (maven-resolver, maven-core, cognitect-aws, jetty) off any classpath that only loads this namespace without calling it -- specifically the Leiningen plugin classloader, which loads this ns transitively (via skeptic.cli.main) during self-analysis but never invokes deps.edn path discovery.

Eligibility filter: a discovered .clj/.cljc/.cljs file is sent to the worker iff every dep symbol in its ns-form's :require/:require-macros/ :use/:use-macros clauses is either a namespace defined by another in-project source file OR is resolvable as a classpath resource against a URLClassLoader built from the basis's full classpath. Rejection is closed under transitive project-internal requires: when G is rejected, every project file whose ns-form transitively requires G is also rejected. Rejected files never reach the worker and produce a :unresolvable-deps failure entry that becomes a ns-discovery-warning.

Source-path discovery for the deps.edn entrypoint. Reads the project's
deps.edn through the official tools.deps API and returns the merged
:paths vector for the given alias selection. The Leiningen plugin does
not use this; it gets paths from the lein project map.

`clojure.tools.deps` is resolved lazily inside `create-basis` rather than
required at namespace load. That keeps its heavyweight transitive graph
(maven-resolver, maven-core, cognitect-aws, jetty) off any classpath that
only loads this namespace without calling it -- specifically the Leiningen
plugin classloader, which loads this ns transitively (via skeptic.cli.main)
during self-analysis but never invokes deps.edn path discovery.

Eligibility filter: a discovered .clj/.cljc/.cljs file is sent to the
worker iff every dep symbol in its ns-form's :require/:require-macros/
:use/:use-macros clauses is either a namespace defined by another
in-project source file OR is resolvable as a classpath resource against
a URLClassLoader built from the basis's full classpath. Rejection is
closed under transitive project-internal requires: when G is rejected,
every project file whose ns-form transitively requires G is also
rejected. Rejected files never reach the worker and produce a
:unresolvable-deps failure entry that becomes a ns-discovery-warning.
raw docstring

skeptic.cljs.analyzer-driver

Single-form cljs analysis entrypoints via a file-local cljs compiler state.

analyze-form analyzes one already-read cljs form against a supplied ns AST inside a non-leaking compiler state, used by the schema/malli collectors to type a post-macroexpansion declaration body. Whole-file analysis lives on the worker (skeptic.worker.analyzer-cljs); the checker never analyzes cljs source files through this namespace.

cljs ASTs carry :type on :binding/:fn-method nodes that conflicts with skeptic's :type slot (SemanticType), and :binding nodes lack :form. analyze-form strips and synthesizes via normalize-cljs-node so the skeptic annotate pipeline starts from a clean shape.

Single-form cljs analysis entrypoints via a file-local cljs compiler state.

`analyze-form` analyzes one already-read cljs form against a supplied ns AST
inside a non-leaking compiler state, used by the schema/malli collectors to
type a post-macroexpansion declaration body. Whole-file analysis lives on the
worker (`skeptic.worker.analyzer-cljs`); the checker never analyzes cljs
source files through this namespace.

cljs ASTs carry `:type` on `:binding`/`:fn-method` nodes that conflicts
with skeptic's `:type` slot (SemanticType), and `:binding` nodes lack
`:form`. `analyze-form` strips and synthesizes via `normalize-cljs-node`
so the skeptic annotate pipeline starts from a clean shape.
raw docstring

skeptic.cljs.schema-interpreter

sci-sandboxed interpretation of post-macroexpansion Plumatic Schema forms collected from cljs ASTs.

The sci context exposes schema.core as the only allowlisted user namespace; sci interprets the form by applying Plumatic's real JVM functions and returns real Plumatic Schema records. Symbols outside the allowlist (and sci's default clojure.core surface) cannot be resolved, so the interpreter cannot execute arbitrary user code.

SCI is loaded through a private implementation namespace only when a CLJS schema form actually needs interpretation. This keeps skeptic.core and the Lein plugin load path from eagerly loading SCI/edamame.

sci-sandboxed interpretation of post-macroexpansion Plumatic Schema forms
collected from cljs ASTs.

The sci context exposes `schema.core` as the only allowlisted user
namespace; sci interprets the form by applying Plumatic's real JVM
functions and returns real Plumatic Schema records. Symbols outside
the allowlist (and sci's default clojure.core surface) cannot be
resolved, so the interpreter cannot execute arbitrary user code.

SCI is loaded through a private implementation namespace only when a CLJS
schema form actually needs interpretation. This keeps `skeptic.core` and the
Lein plugin load path from eagerly loading SCI/edamame.
raw docstring

skeptic.cljs.sci-schema-interpreter

SCI-backed implementation for skeptic.cljs.schema-interpreter.

Keep this namespace out of eager host/plugin load paths. It requires SCI directly because SCI's namespace-copy helpers are macros.

SCI-backed implementation for `skeptic.cljs.schema-interpreter`.

Keep this namespace out of eager host/plugin load paths. It requires SCI
directly because SCI's namespace-copy helpers are macros.
raw docstring

skeptic.cljs.topo

Dependency ordering for cljs/cljc source files. Returns files in an order where each file's project-local :require'd dependencies appear before it. When a cycle blocks standard topo progress, the next pick is chosen by tiebreaker: nss without :require-macros / :use-macros first, then fewest :requires, then ns-sym alphabetical.

Ns-head data (:name / :requires / :require-macros / :use-macros) is supplied by head-fn, which reads each file on the worker under the project basis. This namespace never loads cljs sources itself.

Dependency ordering for cljs/cljc source files. Returns files in an
order where each file's project-local :require'd dependencies appear
before it. When a cycle blocks standard topo progress, the next pick is
chosen by tiebreaker: nss without :require-macros / :use-macros first,
then fewest :requires, then ns-sym alphabetical.

Ns-head data (`:name` / `:requires` / `:require-macros` / `:use-macros`)
is supplied by `head-fn`, which reads each file on the worker under the
project basis. This namespace never loads cljs sources itself.
raw docstring

skeptic.core-fns

No vars found in this namespace.

skeptic.host.deps

Single source of truth for Skeptic's HOST runtime coordinates.

The hermetic-host launcher (lein-skeptic) resolves these via its build system (lein's aether wrapper) and uses them as the host JVM's -cp. The deps.edn path (clj -T:skeptic check) uses the equivalent alias declaration in skeptic/deps.edn.

Parallel to skeptic.worker.deps/worker-deps for the worker JVM.

tools.reader is explicitly pinned at >= 1.0.0-beta3 (the first version whose SourceLoggingPushbackReader is Closeable). Pre-1.0.0-beta3 the host's with-open against the reader (skeptic.file/ns-for-clojure-file) reflectively calls .close on a non-Closeable class and crashes — the shape that caused the rc9 plumbing host-side bug.

Single source of truth for Skeptic's HOST runtime coordinates.

The hermetic-host launcher (lein-skeptic) resolves these via its build
system (lein's aether wrapper) and uses them as the host JVM's -cp.
The deps.edn path (`clj -T:skeptic check`) uses the equivalent alias
declaration in skeptic/deps.edn.

Parallel to skeptic.worker.deps/worker-deps for the worker JVM.

tools.reader is explicitly pinned at >= 1.0.0-beta3 (the first version
whose SourceLoggingPushbackReader is Closeable). Pre-1.0.0-beta3 the
host's `with-open` against the reader (skeptic.file/ns-for-clojure-file)
reflectively calls .close on a non-Closeable class and crashes — the
shape that caused the rc9 plumbing host-side bug.
raw docstring

skeptic.malli-spec.collect

Malli intake stream, hermetic from the project JVM. Reads two channels of inert Malli spec data off the worker-shipped clj-state entries — never the live (malli.core/function-schemas) registry or a loaded Var's metadata:

  1. :malli/schema Var-meta → the :malli-schema field the worker captured off the raw source-form (skeptic.worker.server/project-entry).
  2. m/=> → the spec vector at position 2 of the (m/=> sym SPEC) source-form.

Specs are inert keyword/vector data; amb/admit-malli-spec (pinned Malli) does all interpretation host-side. No malli.core require here.

Malli intake stream, hermetic from the project JVM. Reads two channels of
inert Malli spec data off the worker-shipped clj-state entries — never the
live `(malli.core/function-schemas)` registry or a loaded Var's metadata:

1. `:malli/schema` Var-meta → the `:malli-schema` field the worker captured
   off the raw source-form (`skeptic.worker.server/project-entry`).
2. `m/=>` → the spec vector at position 2 of the `(m/=> sym SPEC)` source-form.

Specs are inert keyword/vector data; `amb/admit-malli-spec` (pinned Malli)
does all interpretation host-side. No `malli.core` require here.
raw docstring

skeptic.malli-spec.collect.cljs

ClojureScript admission for Malli function schemas.

Mirrors skeptic.malli-spec.collect/ns-malli-spec-results for cljs source files. Two channels:

  • Var-meta channel: the :malli/schema spec the worker captured off the raw source-form into each cljs entry's :malli-schema field (skeptic.worker.server/project-cljs-entry), exactly as the clj collector reads its entries. The cljs AST never carries user var-meta at [:var :info :meta], so this channel reads the entry field, not the AST.
  • Registration channel: (malli.core/=> sym SPEC) macroexpands to a top-level :op :do whose form is (do (malli.core/-register-function-schema! 'ns 'sym SPEC nil :cljs id) 'ns/sym). Classification walks the outer :form directly.

No cenv reads, no caller-managed compiler state.

ClojureScript admission for Malli function schemas.

Mirrors `skeptic.malli-spec.collect/ns-malli-spec-results` for cljs source
files. Two channels:

- Var-meta channel: the `:malli/schema` spec the worker captured off the
  raw source-form into each cljs entry's `:malli-schema` field
  (`skeptic.worker.server/project-cljs-entry`), exactly as the clj collector
  reads its entries. The cljs AST never carries user var-meta at
  `[:var :info :meta]`, so this channel reads the entry field, not the AST.
- Registration channel: `(malli.core/=> sym SPEC)` macroexpands to a
  top-level `:op :do` whose form is
  `(do (malli.core/-register-function-schema! 'ns 'sym SPEC nil :cljs id) 'ns/sym)`.
  Classification walks the outer `:form` directly.

No cenv reads, no caller-managed compiler state.
raw docstring

skeptic.output.serialize

Coerce a Clojure value to a JSON-safe form for the --debug wire-tap. The only job is to make the value printable through clojure.data.json. No type dispatch, no projection, no filtering. The value is printed by Clojure's own printer under locked bindings — the string it produces is what ships.

Coerce a Clojure value to a JSON-safe form for the --debug wire-tap.
The only job is to make the value printable through clojure.data.json.
No type dispatch, no projection, no filtering. The value is printed by
Clojure's own printer under locked bindings — the string it produces is
what ships.
raw docstring

skeptic.schema.collect.clj-source

Host-side JVM Plumatic admission from worker-captured evaluated schema values. The worker loads the project namespace and reads Var metadata; the host decodes the EDN schema payload and feeds the existing SchemaDesc builder. No source-form schema interpretation and no host project namespace loading.

Host-side JVM Plumatic admission from worker-captured evaluated schema
values. The worker loads the project namespace and reads Var metadata; the
host decodes the EDN schema payload and feeds the existing SchemaDesc builder.
No source-form schema interpretation and no host project namespace loading.
raw docstring

skeptic.schema.collect.cljs

ClojureScript admission for Plumatic Schema declarations.

Mirrors skeptic.schema.collect/ns-schema-results for cljs source files. Operates on per-form analyzed ASTs from skeptic.cljs.analyzer-driver. Reads alias requires from the parsed ns AST (output of cljs.analyzer.api/parse-ns). The cljs analyzer does not attach the s/defn :schema/:arglists Var-metadata to the :def AST node, so the schema forms are read directly from the macroexpansion's :let binding init forms (output-schema* / input-schema*); the single-arity arglist is reconstructed from the s/one arg-name labels in the input-schema form. No cenv reads, no caller-managed compiler state.

Three top-level shapes are recognized:

  • s/def :op :let, single binding output-schema__*__auto__, inner :def at [:body :ret].
  • s/defschema :op :def, :init :form starts with vary-meta.
  • s/defn :op :let, multi-binding outer let with output-schema* and input-schema* gensyms.

Each output-schema* / input-schema* binding init is a symbolic schema form (the cljs analyzer does not evaluate JVM-side); resolution to a real Schema record goes through skeptic.cljs.schema-interpreter, which interprets the form in a sci-sandboxed context that exposes only schema.core.

ClojureScript admission for Plumatic Schema declarations.

Mirrors `skeptic.schema.collect/ns-schema-results` for cljs source files.
Operates on per-form analyzed ASTs from `skeptic.cljs.analyzer-driver`.
Reads alias requires from the parsed ns AST (output of
`cljs.analyzer.api/parse-ns`). The cljs analyzer does not attach the
`s/defn` `:schema`/`:arglists` Var-metadata to the `:def` AST node, so the
schema forms are read directly from the macroexpansion's `:let` binding
init forms (`output-schema*` / `input-schema*`); the single-arity arglist is
reconstructed from the `s/one` arg-name labels in the input-schema form. No
cenv reads, no caller-managed compiler state.

Three top-level shapes are recognized:

- `s/def`        :op :let, single binding `output-schema__*__auto__`,
                 inner :def at [:body :ret].
- `s/defschema`  :op :def, :init :form starts with vary-meta.
- `s/defn`       :op :let, multi-binding outer let with `output-schema*`
                 and `input-schema*` gensyms.

Each `output-schema*` / `input-schema*` binding init is a symbolic schema
form (the cljs analyzer does not evaluate JVM-side); resolution to a real
Schema record goes through `skeptic.cljs.schema-interpreter`, which
interprets the form in a sci-sandboxed context that exposes only
`schema.core`.
raw docstring

skeptic.schema.discovery

Plumatic source-form discovery, hermetic from the project JVM. Classifies each top-level form's head symbol by alias-resolving it against the namespace's (ns …) / top-level (require …) alias map, then tags matches with a producer role.

The forms arrive as inert :source-form data shipped by the worker (the project is never loaded on the host), so heads like s/defn / schema.core/defn / schemy/defn are resolved to the canonical schema.core/defn symbol via the alias map rather than by binding *ns* and consulting live project Vars.

Plumatic source-form discovery, hermetic from the project JVM. Classifies
each top-level form's head symbol by alias-resolving it against the
namespace's `(ns …)` / top-level `(require …)` alias map, then tags matches
with a producer role.

The forms arrive as inert `:source-form` data shipped by the worker (the
project is never loaded on the host), so heads like `s/defn` /
`schema.core/defn` / `schemy/defn` are resolved to the canonical
`schema.core/defn` symbol via the alias map rather than by binding `*ns*` and
consulting live project Vars.
raw docstring

skeptic.schema.wire

Host-side decoder for worker-encoded Plumatic schema values.

Host-side decoder for worker-encoded Plumatic schema values.
raw docstring

skeptic.tool

Clojure CLI tool entrypoints for hermetic deps.edn-side Skeptic runs.

Clojure CLI tool entrypoints for hermetic deps.edn-side Skeptic runs.
raw docstring

skeptic.worker.analyzer-clj

Worker-side clj analyzer execution. Mirrors the env-construction and analyze-form body that live in skeptic.analysis.annotate, with no Skeptic / Schema / Malli dependency. The worker reads the project's own source files with the real Clojure reader and analyzes them in bulk; no form ever crosses host->worker (the host sends only a source-file path).

tools.analyzer.* and tools.reader.* are required eagerly at ns-load (worker boot), under the JVM launch classloader. The launch-classpath order already prefers project entries (worker-classpath-entries in classpath.clj), so the project's pinned tools.analyzer version wins via first-occurrence; no lazy require is needed.

Worker-side clj analyzer execution. Mirrors the env-construction and
`analyze-form` body that live in `skeptic.analysis.annotate`, with no
Skeptic / Schema / Malli dependency. The worker reads the project's own
source files with the real Clojure reader and analyzes them in bulk; no
form ever crosses host->worker (the host sends only a source-file path).

tools.analyzer.* and tools.reader.* are required eagerly at ns-load
(worker boot), under the JVM launch classloader. The launch-classpath
order already prefers project entries (`worker-classpath-entries` in
`classpath.clj`), so the project's pinned tools.analyzer version wins
via first-occurrence; no lazy require is needed.
raw docstring

skeptic.worker.analyzer-cljs

Worker-side cljs analyzer execution. Mirrors the parse + reader-loop that live in skeptic.cljs.analyzer-driver, with no Skeptic / Schema / Malli dependency. The host-side source-file wrapper is rewired to issue a worker RPC instead of running the cljs analyzer locally.

Wire payloads carry the source-file descriptor only; the cljs compiler state never crosses the wire.

cljs.analyzer / cljs.analyzer.api / cljs.env / cljs.compiler are loaded lazily inside with-analysis-bindings so they intern from the project's pinned clojurescript version when present, not Skeptic's runtime-cp version at worker boot.

Worker-side cljs analyzer execution. Mirrors the parse + reader-loop
that live in `skeptic.cljs.analyzer-driver`, with no Skeptic / Schema /
Malli dependency. The host-side source-file wrapper is rewired to issue a
worker RPC instead of running the cljs analyzer locally.

Wire payloads carry the source-file descriptor only; the cljs compiler state
never crosses the wire.

cljs.analyzer / cljs.analyzer.api / cljs.env / cljs.compiler are loaded
lazily inside `with-analysis-bindings` so they intern from the project's
pinned clojurescript version when present, not Skeptic's runtime-cp version
at worker boot.
raw docstring

skeptic.worker.classpath

Worker launch classpath assembly. Skeptic owns its worker runtime as a coordinate declaration in skeptic.worker.deps/worker-deps; the lein and deps.edn callers each resolve those coordinates through their own build system and hand the resolved jar list (worker-jars) plus the project classpath (project-classpath-entries) to worker-classpath-entries. The result is a single launch classpath string: project-cp first, worker jars second, Skeptic's own skeptic.worker.* source entry tail (so the worker JVM can require its own server namespace at boot).

Worker launch classpath assembly. Skeptic owns its worker runtime as a
coordinate declaration in `skeptic.worker.deps/worker-deps`; the lein and
deps.edn callers each resolve those coordinates through their own build
system and hand the resolved jar list (`worker-jars`) plus the project
classpath (`project-classpath-entries`) to `worker-classpath-entries`.
The result is a single launch classpath string: project-cp first, worker
jars second, Skeptic's own `skeptic.worker.*` source entry tail (so the
worker JVM can require its own server namespace at boot).
raw docstring

skeptic.worker.client

Host-side nREPL client: connect to a worker port, send ops, return replies. Uses the Transport protocol directly (send + recv) for synchronous request-response semantics. Does NOT use nrepl.core/client or nrepl.core/message — those layer lazy seqs and timeouts designed for interactive REPLs, not RPC.

nrepl.* namespaces are NOT required at namespace-load time. The worker loads this namespace via server.clj's :require to get loopback-conn and the loopback branch of ask — neither of which touches nrepl. Host-facing functions below defer their nrepl loads via nrepl-resolve, so the project's pinned nrepl wins via the launch classpath's project-first ordering.

Host-side nREPL client: connect to a worker port, send ops, return replies.
Uses the Transport protocol directly (`send` + `recv`) for synchronous
request-response semantics. Does NOT use nrepl.core/client or
nrepl.core/message — those layer lazy seqs and timeouts designed for
interactive REPLs, not RPC.

nrepl.* namespaces are NOT required at namespace-load time. The worker
loads this namespace via server.clj's :require to get `loopback-conn` and
the loopback branch of `ask` — neither of which touches nrepl. Host-facing
functions below defer their nrepl loads via `nrepl-resolve`, so the
project's pinned nrepl wins via the launch classpath's project-first
ordering.
raw docstring

skeptic.worker.deps

Single source of truth for Skeptic's worker runtime coordinates.

Both runtime entrypoints consume this declaration:

  • leiningen.skeptic resolves it via leiningen's aether wrapper.
  • skeptic.cli.main resolves it via tools.deps.

The 11 coordinates are the namespaces load-bearing for the worker JVM: clojure, clojurescript, tools.analyzer(.jvm), tools.reader, tools.namespace (ns-decl parsing for dependency-ordered loading), core.cache/memoize, data.priority-map, transit-clj, nrepl. Whatever each build system transitively resolves from this set is the worker's runtime universe under that build system.

skeptic/project.clj's :worker profile and skeptic/deps.edn's :worker alias mirror this vector for ad-hoc invocations like lein with-profile +worker classpath and clj -A:worker; the runtime code paths read FROM this var, not from those build-file declarations.

Single source of truth for Skeptic's worker runtime coordinates.

Both runtime entrypoints consume this declaration:
  - `leiningen.skeptic` resolves it via leiningen's aether wrapper.
  - `skeptic.cli.main` resolves it via tools.deps.

The 11 coordinates are the namespaces load-bearing for the worker JVM:
clojure, clojurescript, tools.analyzer(.jvm), tools.reader,
tools.namespace (ns-decl parsing for dependency-ordered loading),
core.cache/memoize, data.priority-map, transit-clj, nrepl.
Whatever each build system transitively resolves from this set is the
worker's runtime universe under that build system.

`skeptic/project.clj`'s `:worker` profile and `skeptic/deps.edn`'s
`:worker` alias mirror this vector for ad-hoc invocations like
`lein with-profile +worker classpath` and `clj -A:worker`; the runtime
code paths read FROM this var, not from those build-file declarations.
raw docstring

skeptic.worker.process

Host-side worker process lifecycle: spawn a JVM running skeptic.worker.server, read the port handshake off its stdout, and tear it down. The caller passes a single launch classpath assembled by skeptic.worker.classpath — project-cp first, worker jars second, Skeptic's own worker source tail.

Host-side worker process lifecycle: spawn a JVM running skeptic.worker.server,
read the port handshake off its stdout, and tear it down. The caller passes
a single launch classpath assembled by `skeptic.worker.classpath` —
project-cp first, worker jars second, Skeptic's own worker source tail.
raw docstring

skeptic.worker.server

Worker-side nREPL server. Runs in the spawned JVM on the combined project-first launch classpath and answers host requests on demand. Project operations run on the clojure.main launch thread, so project code loads exactly as the project's own runtime loads it — Skeptic adds no reader-Var or loading machinery. Plan 2 Phase 1.5 adds the handle-table machinery: every Class operand on the wire is an opaque handle (integer for bootstrap-interned host-runtime classes; UUID-string for project classes). Worker is sole owner of Class/forName, .isAssignableFrom, instance?, and class equality.

Phase 5 adds the analyzer ops. The analyzer-execution glue lives in skeptic.worker.analyzer-clj / skeptic.worker.analyzer-cljs. No other skeptic.* namespace is required from this server: Skeptic's own analysis code and Plumatic Schema / Malli stay on the host (B3/B4).

nREPL is lazy-loaded inside start! from the worker JVM's single launch classloader (project-cp first, worker jars second). The ns form does NOT require any nrepl.* namespace at load time — projects pinning a different nrepl win their version via first-occurrence on the launch cp. The wrap-* dispatchers are let-bound inside start!, closing over locally resolved nrepl.transport/send, nrepl.misc/response-for, and nrepl.server/{start-server,unknown-op}. Descriptor metadata is dead code under the explicit handler thread and has been removed.

Worker-side nREPL server. Runs in the spawned JVM on the combined
project-first launch classpath and answers host requests on demand. Project
operations run on the clojure.main launch thread, so project code loads
exactly as the project's own runtime loads it — Skeptic adds no reader-Var
or loading machinery. Plan 2 Phase 1.5 adds the handle-table machinery: every Class
operand on the wire is an opaque handle (integer for bootstrap-interned
host-runtime classes; UUID-string for project classes). Worker is sole owner
of `Class/forName`, `.isAssignableFrom`, `instance?`, and class equality.

Phase 5 adds the analyzer ops. The analyzer-execution glue lives in
`skeptic.worker.analyzer-clj` / `skeptic.worker.analyzer-cljs`. No other
`skeptic.*` namespace is required from this server: Skeptic's own analysis
code and Plumatic Schema / Malli stay on the host (B3/B4).

nREPL is lazy-loaded inside `start!` from the worker JVM's single launch
classloader (project-cp first, worker jars second). The ns form does NOT
require any nrepl.* namespace at load time — projects pinning a different
nrepl win their version via first-occurrence on the launch cp. The
`wrap-*` dispatchers are let-bound inside `start!`, closing over locally
resolved `nrepl.transport/send`, `nrepl.misc/response-for`, and
`nrepl.server/{start-server,unknown-op}`. Descriptor metadata is dead
code under the explicit handler thread and has been removed.
raw docstring

skeptic.worker.transport

Length-prefixed Transit+msgpack transport for Skeptic's private nREPL worker link. The Transport protocol is implemented in the peer namespace skeptic.worker.transport-impl, which is required lazily on first transit call so neither nrepl.transport nor the deftype's Protocol symbol resolves at this namespace's load time.

Length-prefixed Transit+msgpack transport for Skeptic's private nREPL
worker link. The Transport protocol is implemented in the peer namespace
`skeptic.worker.transport-impl`, which is required lazily on first
`transit` call so neither nrepl.transport nor the deftype's Protocol
symbol resolves at this namespace's load time.
raw docstring

skeptic.worker.transport-impl

Holds the deftype that implements nrepl.transport/Transport over the Transit+msgpack framing helpers in skeptic.worker.transport. This namespace is required lazily by skeptic.worker.transport/transit so nrepl.transport's Transport protocol resolves at the project's pinned version, not Skeptic's runtime-cp version at worker boot.

Holds the deftype that implements `nrepl.transport/Transport` over the
Transit+msgpack framing helpers in `skeptic.worker.transport`. This
namespace is required lazily by `skeptic.worker.transport/transit` so
nrepl.transport's Transport protocol resolves at the project's pinned
version, not Skeptic's runtime-cp version at worker boot.
raw docstring

No vars found in this namespace.

skeptic.worker.wire

Host-safe wire-contract constants shared by both JVMs. Holds ONLY the keys and accessors for values that cross the worker->host AST boundary; carries no nREPL, tools.analyzer, or other worker-classpath dependency, so the host may require it without re-coupling to worker-only code.

The non-EDN sentinel: a raw analyzer-AST :val/:form/:raw-forms leaf outside the wire-safe set (plain EDN scalars/colls plus the transit-carried leaves: char, UUID, exact java.util.Date) is shipped as {::nonedn true ::class <class-handle>} — regex Patterns, fn objects, Vars, Namespaces, and any project-runtime object a data reader produced (a joda DateTime from a tagged literal). The host types it by its class via the carried handle; it never inspects the original value. The transport's default-handler backstop ships anything that slips past projection as {::nonedn true ::class-name <name> ::string <print>} — same host typing, name resolved to a handle lazily.

Form metadata: the worker captures the host-read meta keys off each form into a plain data vector in clojure.walk/postwalk order (capture-form-meta); the host replays them onto the structurally-identical received form in the same order (apply-form-meta). Shape is never altered, so structural form walks survive.

Host-safe wire-contract constants shared by both JVMs. Holds ONLY the keys
and accessors for values that cross the worker->host AST boundary; carries no
nREPL, tools.analyzer, or other worker-classpath dependency, so the host may
require it without re-coupling to worker-only code.

The non-EDN sentinel: a raw analyzer-AST `:val`/`:form`/`:raw-forms` leaf
outside the wire-safe set (plain EDN scalars/colls plus the transit-carried
leaves: char, UUID, exact java.util.Date) is shipped as
`{::nonedn true ::class <class-handle>}` — regex Patterns, fn objects, Vars,
Namespaces, and any project-runtime object a data reader produced (a joda
DateTime from a tagged literal). The host types it by its class via the
carried handle; it never inspects the original value. The transport's
default-handler backstop ships anything that slips past projection as
`{::nonedn true ::class-name <name> ::string <print>}` — same host typing,
name resolved to a handle lazily.

Form metadata: the worker captures the host-read meta keys off each form into
a plain data vector in `clojure.walk/postwalk` order (`capture-form-meta`);
the host replays them onto the structurally-identical received form in the
same order (`apply-form-meta`). Shape is never altered, so structural form
walks survive.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close