# Clj Desk > Paste one Clojure namespace - and where you have it the test namespace beside > it - and work it through four lanes over that same file: the style-guide > review, the docstring contracts, the Malli schemas its API surface is missing, > and the mutation coverage its tests claim. A free in-browser Clojure reader > parses the file itself first: its own tokenizer and form reader, so a string > full of parens, a character literal, a regex literal, a `#_` discard and a > `#?()` reader conditional are each read as what they are. Live at https://clj-desk.skillsafe.ai/ ## What it is for One work object: a single Clojure namespace, optionally with its test namespace pasted beside it. Four lanes over that file, in the order a developer meets them before opening a pull request: 1. `review` - Style review. Would you approve this? Grades every definition in file order on naming (nouns for pure functions, `!` for side effects, `?` for predicates, kebab-case, no `clojure.core` shadowing), docstrings on public vars, private-by-default, form length and nesting, the style guide's whole "use that instead" table, and the REST, module and Toucan conventions where the namespace has them. Posture: `mergeable`, `mergeable-with-changes`, `needs-rework`. Table: `vars[]`. 2. `docstrings` - Docstring contracts. Reads the prose on its own, away from the code that flatters it. Every sentence goes through two tests: the **rewrite test** (if a different implementation with the same contract could make it false, it describes the implementation, not the contract) and the **ownership test** (if it states a fact about code this function does not own, it survives any rewrite trivially). Implementation context is **relocated** to an inline comment at the line it applies to, not deleted; only blather is deleted - self-congratulation, restating the obvious, or narrating a property that is the expected default. Each var gets a verdict and the full replacement text. Posture: `contracts-clear`, `contracts-mixed`, `contracts-misleading`. Table: `docstrings[]`. 3. `schemas` - Malli schemas. Writes the schema for every place one belongs: route params, query params, body, response - or, in a namespace with no endpoints, the `fn-arg` and `fn-return` schemas of its `mu/defn` surface. Prefers the `ms/*` types that cooperate with coercion over bare predicates, uses `mr/def` for any shape used twice, and checks the validation-timing trap first: a response schema validates *before* JSON serialisation, so a temporal field there is `:any` and a hydrated set is `[:set X]`, never `ms/TemporalString` or `[:sequential X]`. Posture: `fully-specified`, `partly-specified`, `unspecified`. Table: `schemas[]`. 4. `mutants` - Mutation coverage. Not "are there tests" but "would the tests fail if this decision were wrong?" Generates named mutants per operator - conditional boundary, negated conditional, arithmetic, constant, boolean literal, return value, collection, guard removal - and marks each `killed`, `survived` or `uncertain` against the assertions actually pasted. A `killed` row must name the `deftest` that fails; if it cannot, the status is not `killed`. The artifact is `deftest` forms that kill the survivors. Posture: `well-covered`, `partly-covered`, `weakly-covered`. Table: `mutants[]`. The handoff between lanes is a button: the same namespace carries across, so the review leads into the docstrings, the docstrings into the schemas, and the schemas into the mutation coverage without re-pasting. ## What the free in-browser reader computes No model call, no sign-in, no charge. It is a real Clojure reader, not a set of regexes, which is why its counts can be trusted. - **Tokenizing that gets Clojure right**: strings with escapes, character literals including `\(` and `\newline`, regex literals `#"..."` whose escaped quote ends nothing, `#_` discards, `#'` var quotes, `#{}` sets, `#()` anonymous functions, `#?(:clj ...)` and `#?@()` reader conditionals, `#:ns{}` and `#::alias{}` namespaced maps, `#tag` literals, metadata, quote, syntax-quote, unquote and deref. The `#=` read-time eval reader is reported as a blocker. - **The delimiter that went missing**: the line and column of the *opener* that was never closed, and whether a `)` is closing something a `[` opened - rather than the compiler's report at the end of the file. - **The ns form, resolved**: every require with its alias, `:refer :all`, a libspec declared twice where the later one silently wins, a deprecated `:use`, a require with no alias at all, and every alias declared in the ns form that nothing in the pasted code uses. - **The definition inventory**: kind, visibility (including `^:private` metadata and the `defn-` spelling), arglists with multi-arity and variadic handled correctly, docstring presence and size, body length, nesting depth and the named schema for `defn`, `defn-`, `def`, `defonce`, `defmacro`, `defmulti`, `defmethod`, `defprotocol`, `defrecord`, `deftype`, `deftest`, `mu/defn`, `mr/def` and `defendpoint`. - **The mutation surface, counted exactly**: branch points, comparisons, arithmetic operators, boolean literals and numeric literals per function, and the number of mutants a runner would generate from them. - **Style anti-patterns as form shapes, not substring matches**: `(if (not x))`, `(when (not x))`, `(= nil x)`, `(= 0 (count x))`, `(not (empty? x))`, `(first (filter ...))`, `(first (sort ...))`, `(apply str (interpose ...))`, `#(do ...)`, `(fn [x] (f x))`, a `let` that binds and immediately returns, a `def` inside a function body, a top-level `atom`, `with-redefs`, `Thread/sleep`, `declare`, and the keyword form of a clj-kondo suppression that the linter silently ignores. - **Naming**: snake_case and camelCase vars, a name that shadows `clojure.core`, a pure function named as a verb, a function that mutates without a `!`, a `!` with nothing behind it, and a name that repeats its own namespace's last segment. Database reads are recorded as I/O facts and never used to demand a `!`. - **Docstring smells**: a public var with none, one over six lines or 600 characters, a reference in backticks instead of `[[var]]`, and prose containing the phrases that usually narrate the implementation or restate a default. - **Endpoints**: a `defendpoint` with no response schema, none of whose params carry a `:-`, a plural route noun, a snake_case query param, an `{:optional true}` entry not wrapped in `[:maybe ...]`, and `ms/TemporalString` where a response schema needs `:any`. - **Test cross-reference**: which public functions with a real mutation surface are never named in the pasted tests, `deftest` names that do not end in `-test`, tests with no `^:parallel`, and assertion counts per test. - **Lines and comments**: over 120 columns, trailing whitespace, tab indentation, a missing final newline, and `TODO`/`FIXME` without an author and a date. Every flag it raises carries a stable id, and the paid lane must return one `coverage_check` entry per id. The app displays that reconciliation as a table and names any flag the run failed to address - which is the point of having both halves. As you edit the file, the flags that cleared, are still open, or are newly introduced since the reviewed baseline are recounted live in the browser, for free. ## Contract Input is the object itself (not wrapped in an `input` key): {"task": "review" | "docstrings" | "schemas" | "mutants", "source": "the Clojure text", "notes": "what you are about to do with it", "prescan": {"facts": ..., "flags": [...], "counts": ...}} Two namespaces in one `source` string is the normal case: the reader groups the top-level forms under whichever `ns` form precedes them, so a source file and its test file pasted back to back are told apart by content. A `;; file: name.clj` line is honoured as a separator but is never required. Over 60,000 characters the source is cut on **whole top-level form boundaries** - never mid-expression - and a `clip_note` names every form that was not sent. Output is one JSON object: a common envelope shared by every lane - `task`, `title`, `posture`, `confidence`, `verdict`, `exec_summary`, `findings[]`, `coverage_check[]`, `artifacts[]`, `assumptions[]`, `open_questions[]`, `next_steps[]`, `summary` - plus exactly one lane body: `vars[]` for review, `docstrings[]` for docstrings, `schemas[]` for schemas, `mutants[]` for mutants. Full API documentation, with worked examples in cURL, Python, JavaScript, Go, Java, Ruby, PHP and C#: https://clj-desk.skillsafe.ai/api.html ## Boundaries, stated plainly This app reads Clojure text and returns judgement over it. There is no REPL, no nREPL, no clj-kondo, no test runner and no mutation harness behind this page. Nothing is evaluated, compiled, linted or measured by running it. A mutant's status is a read of the assertions you pasted, not the result of a run - which is exactly why the prompt forbids claiming otherwise and why an undecidable status is reported as `uncertain` rather than guessed. Some conventions are the Metabase house style rather than universal Clojure - the module layout, the `ms/*` schema aliases, Toucan 2, `api.macros/defendpoint`. The result says which, and where the pasted code is plainly not a Metabase namespace the general Clojure rule is applied instead. Runs are metered and need a signed-in SkillSafe account. Estimating any lane is free, and both bundled examples ship with a saved result for every lane, so all four can be seen end to end without an account and without a charge. ## Sources A derived work built on four agent skills published by the Metabase team, from the `metabase/metabase` repository: - @metabase/clojure-review - reviewing Clojure and ClojureScript for compliance with the house style guide: naming, documentation, code organization, tests, module boundaries, REST API conventions, database and driver conventions, and a pattern table of common violations. - @metabase/clojure-write - REPL-driven development conventions, and the docstring discipline this app's docstring lane is built on: the rewrite test, the ownership test, relocate-rather-than-delete, and say-each-fact-once. - @metabase/add-malli-schemas - adding Malli schemas to API endpoints uniformly: route, query and body params, response schemas, the `ms/*` schema types, validation timing, named schemas via `mr/def`, and custom error messages. - @metabase/mutation-testing - mutation testing a Clojure namespace and writing the tests that kill the surviving mutations. Its orchestration half - the nREPL session, the issue tracker and the pull requests - is deliberately not part of this app; only the reasoning is. This app is not a republication of those skills. Model: gpt-terra (currently resolving to gpt-5.6-terra).