Skip to content
Back to skills

Dsail

ASecurity

Check documents, decisions and AI output against a written policy, and get the same answer every time. Use when asked to enforce, check or encode a policy, rule, threshold or compliance requirement against facts extracted from documents, as a policy rules engine or as decision rules compiled from the policy text: policy compliance and regulatory compliance checks, compliance policy evaluation and compliance checklists with a decision audit trail, contract compliance and trade compliance, gift...

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 25, 2026
ai-agentspythongoexpressapidocumentation

Works with

  • terminal
  • cli
  • api
  • mcp

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 7, 2026

npx -y skills add JaxonAI/dsail --skill dsail --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dsail?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dsail
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jaxonai-dsail/badge)](https://www.skillsdirectory.com/skills/jaxonai-dsail)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dsail
description: "Check documents, decisions and AI output against a written policy, and get the same answer every time. Use when asked to enforce, check or encode a policy, rule, threshold or compliance requirement against facts extracted from documents, as a policy rules engine or as decision rules compiled from the policy text: policy compliance and regulatory compliance checks, compliance policy evaluation and compliance checklists with a decision audit trail, contract compliance and trade compliance, gifts and entertainment pre-clearance, adverse action reasons, fee schedules, KYC and AML rules, suitability, underwriting eligibility, insurance rating rules and claims adjudication, medical necessity criteria, export control and clause flowdown, open source license policy, release gates and change freeze rules, records retention schedules. Every result names the rule that decided, and the check runs on the hosted DSAIL service with no model in the loop."
---

# DSAIL: policy to formal ruleset, with reproducible results

DSAIL turns a written policy into a formal ruleset and returns deterministic, reproducible results over claim values you extract: given these claim values under this ruleset, every assertion answers TRUE, FALSE, UNKNOWN or AMBIGUOUS — every time, and every result names the rule that decided. Whether the claim values faithfully describe the document is your extraction's responsibility. The service never calls a language model; you run extraction on the user's own model using the prompt pack it generates. Report results in those four words, attributed to the rules, and never as an overall verdict of your own.

Documentation for agents, every page as markdown: https://docs.agents.jaxon.ai (`https://docs.agents.jaxon.ai/llms.txt`
lists them all). Every structured error the service returns carries `docs`, the
URL of the page that resolves it — fetch it before retrying blind.

## How to work in this repository

- Rules live in the repo. Write the English policy summary and the DSAIL source
  as files next to the code they govern (for example `policies/<name>.md` and
  `policies/<name>.dsail`), commit them, and review them like code.
- The hosted service compiles and checks; nothing formal runs locally. Use the
  `dsail_*` MCP tools if they are connected, otherwise the CLI:

      dsail compile policies/<name>.dsail          # hash, manifest, review text
      dsail prompt-pack --hash <ruleset_hash>       # the extraction contract
      dsail check --hash <ruleset_hash> --claims claims.json
      dsail serve policies/<name>.dsail             # review UI at a localhost link

- No `dsail_*` tools in this session (a cloud coding task, for one, has
  no MCP layer at all) means the CLI and the Python client ARE the path, not a
  fallback: the same operations, the same service, the same results. If
  `dsail` is not installed, `pip install dsail` first.
- Review is a human step. If `dsail_open_review` is in your tool list (the
  local `dsail mcp` proxy), call it with the source, file path or stored name:
  it starts the review UI on this machine, opens the browser and returns a
  link — repeat that link to the user. If it is not (the hosted server), call
  `dsail_review` once after a successful compile: a client that renders it
  shows an inline review widget. A client that renders no widget gets text
  only; say so, and give the user the exact command
  `dsail serve policies/<name>.dsail` to run in their own terminal (after
  `pip install dsail`). Never say a review panel is open unless one rendered or
  a link came back. Approval is recorded on the service against the exact
  hash.
- Integration code fetches the prompt pack, runs extraction on this project's
  own model and credentials, submits the claim dictionary with `dsail.Client`
  (`check_with_repair` handles validation failures), and acts on the
  per-assertion results. Jaxon never sees documents, keys or model choices.
- If a call fails saying outbound network access is blocked, relay that
  message to the user verbatim: it names the one-time fix for this environment.

Authoring sequence — follow it in this order:

1. dsail_compile(source)         -> ruleset_hash, claim manifest, claim schema,
                                    validation contract, diagnostics.
                                    On failure, read hint and fix the source.
2. dsail_get_prompt_pack(hash)   -> one extraction prompt per claim, plus the
                                    schema and the exact validation rules.
                                    You run the extraction on the user's own
                                    model; this service never calls an LLM.
3. dsail_check(hash, claims)     -> every rule with each assertion's own result
                                    (TRUE / FALSE / UNKNOWN / AMBIGUOUS), named
                                    and with its source text. One call
                                    validates AND solves.
4. dsail_save_ruleset(name, src) -> a named, immutable revision (parent-linked).
5. dsail_record_approval(hash)   -> binds a human approval to that exact hash.

Presenting: once step 1 SUCCEEDS, call dsail_review(hash) exactly once to show
the user the ruleset in the review widget (where it is also available). Never
call it for a compile that failed — fix the source and compile again; those
iterations are yours alone and render nothing. Call it again only for a later
revision you want the user to see.

Every object is addressed by the content hash of its source, so a hash is
proof of exactly which bytes produced a result.

BEFORE step 1, for every numeric claim: does the quantity have a unit? Money,
distance, weight, duration, data size — all do. If it does, declare it:

    // @unit amount USD
    declare amount as numeric;

This is not documentation. A numeric claim with no declared unit accepts only
bare numbers, so an extractor that answers "30000 EUR" is refused rather than
compared — and a policy written about dollars whose claim says nothing about
dollars cannot tell dollars from anything else. Declare the unit and the engine
converts what converts (2500 m against a km threshold), refuses what does not,
and tells you which pairs nothing bridges. Call dsail_unit_library if you are
unsure whether two units convert; never assume a currency rate.

THEN WRITE THAT UNIT ON EVERY LITERAL THE CLAIM IS COMPARED WITH. This is
enforced: a ruleset that compares a united claim against a bare number does not
compile.

    assert cap { amount <= 25000 "USD" };      -- compiles
    assert cap { amount <= 25000 };            -- REFUSED

Units live on literals, not on `declare`, and a bare literal adopts the unit of
whatever it meets. So the second form is not "25000 dollars": bind an answer of
24000 "EUR" and the threshold becomes 25000 EUR, the policy quietly
redenominates itself to the evidence, and 24000 <= 25000 comes back compliant
where the real question — is 25920 USD over 25000 USD — is not. Writing the
unit costs four characters and moves every conversion inside the solver, where
it is done in exact rationals.

Zero is not exempt: write 0 "USD". A dimensionless zero looks safe because
scaling leaves it at zero, but not every conversion is a scaling — 0 degC is
32 degF — and the rule is easier to follow than its exceptions.

Leave a number unsigned only when the quantity truly has no dimension: a count
of signatures, a position in an ordering, a ratio, a boolean-ish 0/1.

DSAIL ruleset grammar (v1.3), the subset this service compiles to SMT:

  version 1.3;                       -- optional; 1.2 and 1.3 are accepted

  declare <name> as boolean;         -- a yes/no claim
  declare <name> as numeric;         -- a number; units go on literals (below)
  declare <name> as enum ["a","b"];  -- ORDERED vocabulary (comparable with < >)
  declare <name> as enum {"a","b"};  -- UNORDERED vocabulary (== and != only)
  declare local <name> as boolean;   -- rule-local; NOT a claim, never extracted

  assert <name> { <expr> };              -- the rule. Holds => compliant.
  assert <name> [pessimistic] { ... };   -- unknown-resolution policy:
                                         -- optimistic | pessimistic | neutral

Enum members are double-quoted strings. Every statement ends with a semicolon.
You do not write `let` bindings for claim values — this service injects them
from the claim dictionary you submit to check.

Literals:
  numbers        42   3.5   0.05
  with a unit    3000 "mi"   25000 "USD"   0 "USD"
                 -- a number followed by the unit as a QUOTED string. A bare
                 -- unit word is a syntax error: 3000 mi fails to parse.
  booleans       True   False
                 -- capitalised. Lowercase true and false are read as
                 -- identifiers and fail as undefined variables.
  enum members   "low"   "high"            -- double-quoted strings

Expressions:
  comparison   ==  !=  <  <=  >  >=      (thresholds are STRICT: > means
                                          strictly greater, NOT at-or-above.
                                          Write >= if you mean at-or-above.
                                          Equality is ==, not =.)
  arithmetic   +  -  *  /  %
  LOGIC IS FUNCTION-STYLE, NOT INFIX. There is no `and`/`or`/`not` keyword:
               And(a, b, ...)     Or(a, b, ...)     Not(a)
               Xor(a, b)          Implies(a, b)     If(cond, a, b)
  conditional  IF <cond> THEN <expr> [ELSE <expr>] END
               CASE <subject> OF "x": <expr>, "y": <expr>, DEFAULT: <expr> END
  quantifiers  ForAll(x in s, p)      Exists(x in s, p)
               AtLeast(n, x in s, p)  AtMost(n, x in s, p)
               ExactlyOne(x in s, p)  CountWhere(x in s, p)
  sets         IsMember(v, s)  IsSubset(a, b)
               Union(a, b)  Intersect(a, b)  Difference(a, b)

What a check answers. Every assertion gets its own result, and that is the
whole answer:

  TRUE       the assertion holds
  FALSE      the assertion is violated
  UNKNOWN    a claim it needs was submitted as unknown, or is unconstrained
  AMBIGUOUS  reserved. It means an assertion's evidence contradicted
             itself, which needs several answers for one claim. A claim
             dictionary holds exactly one value per claim, so no check
             submitted to this service produces it. Handle the word if you
             switch on results; there is no input that triggers it.

There is no combined verdict, no severity and no ranking. What a violated
assertion should cost — a refusal, a human review, a note in a file — is a
decision for the system reading the result, and this service does not make it.

Host annotations. These are ordinary DSAIL comments, invisible to the compiler,
read by this service. Every one of them describes a CLAIM — what to ask for it
and what a valid answer looks like. None of them decides an outcome:

  // @ask <claim> What is ...?       -- the question shown to an extractor
  // @context <claim> <text>         -- extra context for the extractor
  // @range <claim> 0..100           -- numeric bounds, enforced at check time
  // @unit <claim> USD               -- expected unit for a numeric claim

@effect, @effect-default and @consistency are NOT recognised. A ruleset
carrying one does not compile.

Rule descriptions. Directly above every assert, with no blank line between,
write what the rule requires in plain words as an ordinary comment block (no
@ tag). The review widget shows it as the rule's description and a person
edits it there, so it must say what the assert beneath it actually checks.
dsail_compile over MCP refuses an assert that has none, naming its line.

Worked example:

  version 1.3;
  // @ask loanAmount What is the loan principal, in USD?
  // @unit loanAmount USD
  // @range loanAmount 0..100000000
  declare loanAmount as numeric;
  // @ask hasAppraisal Does the file contain a completed appraisal?
  declare hasAppraisal as boolean;
  declare riskTier as enum ["low","medium","high"];

  // No loan may exceed one million dollars.
  assert within_cap { loanAmount <= 1000000 "USD" };
  // A loan over 500,000 USD needs an appraisal on file.
  assert large_but_documented { Or(loanAmount <= 500000 "USD", hasAppraisal) };
  // A loan over 250,000 USD needs an appraisal on file.
  assert appraisal_when_large {
    IF loanAmount > 250000 "USD" THEN hasAppraisal ELSE True END
  };
  // High-risk applicants are not permitted.
  assert tier_permitted { riskTier != "high" };

Integrity rule: unknown is a first-class answer. If you cannot determine a claim's value from the evidence, submit the string "unknown" (or JSON null). Never guess, never substitute a type-correct placeholder to satisfy a validator, and never omit the claim. A guessed value produces a confident TRUE or FALSE about a situation that does not exist, which is worse than no answer; "unknown" produces an honest UNKNOWN.

<!-- generated by dsail 1.0.6; wire contract 2.10.0; re-run `dsail init` to refresh -->

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…