Skip to content

docs: add proposed server-side policy evaluation tutorial - #338

Draft
pbeckham wants to merge 22 commits into
mainfrom
6267-evaluate-policy-tutorial
Draft

docs: add proposed server-side policy evaluation tutorial#338
pbeckham wants to merge 22 commits into
mainfrom
6267-evaluate-policy-tutorial

Conversation

@pbeckham

Copy link
Copy Markdown
Contributor

What

Documentation-driven development for the server-side Kosli Evaluate project (kosli-dev/server#6267). Adds a tutorial describing the proposed V1 kosli evaluate policy flow, published so it can be shared internally and with design-partner customers for feedback before the feature is built.

This is a design preview — nothing in it is runnable today. The page is marked as such throughout.

Scope (this draft)

Core happy path for a governance platform engineer:

  • Author a policy locally — Rego with name/purpose/input-context schema (allow mandatory).
  • Ship a versioned policy into Kosli — CLI/API, identifier + version.
  • Evaluate and record a decisionkosli evaluate policy runs server-side and records a decision against a control; synchronous.
  • Trace and replicate — evaluation context pins trail moments, attestation_ids, policy version, params, timestamp; includes the T / T+1 / T+2 temporal-correctness case.
  • Evaluate a local policy file — decision still recorded server-side.

An explicit out-of-scope-for-V1 section keeps reviewer feedback on target (managing policies, testing/validation, reporting/audit views, unhappy-path inspection, composable policies, non-trail targets, scheduled evals, risk measurement).

Design notes surfaced for feedback

  • Command surface verified against the real kosli attest decision and kosli evaluate trail flags. --compliant and evidence (--attachments/--user-data) are intentionally not flags — the server derives compliance and captures the policy/report/violations as output.
  • The publish command reuses kosli create policy, which collides with the existing environment-policy command — flagged inline as an open question.
  • Rego input shape is borrowed from the client-side tutorial and may differ server-side.

Draft because

Content describes proposed behavior and will change as the design firms up; opening as draft to gather review before it's merged/published.

🤖 Generated with Claude Code

pbeckham and others added 2 commits July 1, 2026 17:05
Update the working-with-controls tutorial to match released behavior
(issue #6005): document requiring decisions in flow templates, the new
Coverage tab and coverage API, control archiving semantics (controls,
environments, and policies), and the controls CLI commands (create,
update, list, get, archive, unarchive, tag). Replace the catalog,
decisions, and decision-detail screenshots and add a coverage screenshot,
all cropped to app content.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Documentation-driven development for the server-side Kosli Evaluate
project (issue #6267): draft a tutorial describing the proposed V1
`kosli evaluate policy` flow so it can be shared internally and with
design partners for feedback before the feature is built.

Covers the core happy path for a governance platform engineer: author
a policy locally, ship a versioned policy into Kosli, evaluate it
server-side to record a trustworthy, replicable decision against a
control, and the local-policy-file variation. Marked throughout as a
design preview (not runnable today) and scoped with an explicit
out-of-scope-for-V1 section to keep feedback on target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pbeckham's task in 3m 3s —— View job


Review: Server-side policy evaluation tutorial

  • Read CLAUDE.md and doc-review skill
  • Gather PR diff and read changed files
  • Check navigation config for new pages
  • Review content quality, structure, and conventions
  • Post inline comments on specific issues
  • Post final summary

Summary

Strong design-preview tutorial that clearly separates what's proposed from what exists today. The <Warning> banner, the "Why server-side" comparison table, and the explicit "out of scope" section are well done — they set the right expectations for a design preview. The working_with_controls.mdx updates (CLI commands, coverage tab, archiving, flow template section) are clean additions.

Findings

By file: tutorials/evaluate_policies_server_side.mdx

Category Issue
Improvement Meta description ~170 chars — trim to ≤160 for search engines.
Improvement Intro/structure mismatch — five numbered parts listed, but only part 1 uses <Steps>. Parts 2–5 read as reference sections, not tutorial steps. Reframe the intro to set that expectation.
Improvement kosli create policy collision not called out explicitly — the <Info> callout hints at the relationship but doesn't name the naming clash. For a design preview seeking feedback, make it unmissable.
Improvement Async/--sync/--assert restated — paragraph at lines 204–206 repeats what the intro and flags table already say. Remove it.
Suggestion --trail flag undocumented by example — appears in the flags table but not in any command example. A brief usage note would help.
Improvement Placeholder images invisible on preview — all three policy-*.png files are 70-byte transparent PNGs. They render as empty frames on the Mintlify preview. Use visible placeholders or remove the <Frame> blocks until real screenshots are ready.

By file: tutorials/working_with_controls.mdx

No new issues. The additions (CLI examples, archiving section, coverage tab, flow template section) are well-structured and consistent with the existing page.

By file: config/navigation.json

No issues. tutorials/evaluate_policies_server_side is correctly listed under the "Evaluation" subgroup.

CLAUDE.md compliance

Rule Status
Root-relative links only All links are root-relative
New page added to config/navigation.json Yes — under Tutorials > Evaluation
YAML frontmatter present Yes — title and description
Writing style (active voice, imperative mood) Good throughout
American spelling Consistent

Diátaxis assessment

The page is titled as a tutorial but is a hybrid: part 1 (the <Steps> section) is a genuine tutorial walk-through, while parts 2–5 read more like reference/how-to material. This is fine for a design preview — but if the page ships to production, consider splitting or reframing to make the boundary explicit.

Overall

6 findings (0 critical, 4 improvement, 2 suggestion). The content is well-written, the design-preview framing is clear, and the compliance checks pass. The main action items are tightening the structure/intro mismatch and making the kosli create policy naming collision explicit for reviewers.

@mintlify

mintlify Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
kosli 🟢 Ready View Preview Jul 23, 2026, 1:43 PM

Comment thread tutorials/evaluate_policies_server_side.mdx Outdated
Comment thread tutorials/evaluate_policies_server_side.mdx Outdated
- Replace internal "V1" version label with external "beta" phrasing
- Clarify today's client-side flow: Kosli only records the outcome if
  you explicitly attest it back, not automatically
- Add data-processing/performance advantage to "Why server-side"
  (evaluate in the cloud, no need to ship full trail data to the client)
- Remove the temporal-correctness (T/T+1/T+2) note
- Evaluation context stores trail moments, not explicit attestation_ids
- Correct the sync model: async by default, with --sync, and --assert
  implying synchronous behavior
- Tighten the out-of-scope list to the genuinely-excluded set and add
  policy-to-control association; note the rest of the beta flow is in
  scope and will be documented later

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
<Step title="Evaluate and record a decision">

`kosli evaluate policy` is the core command. Kosli fetches the in-scope facts, runs the policy
**server-side**, and records a **decision** against a control. Evaluation is **asynchronous by

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: The intro says evaluation is "asynchronous by default" (line 144) and this is repeated almost verbatim in the paragraph at lines 181-183. Consider removing one to avoid the redundancy — the flag table already documents both --sync and --assert.

Comment thread tutorials/evaluate_policies_server_side.mdx Outdated
Add the remaining in-scope beta capabilities as flat sections after the
core-loop steps:

- Validate and test a policy (kosli validate policy; --dry-run ad-hoc
  evaluation that returns an outcome without recording a decision)
- Manage policies (update/versioning, tagging, archive/unarchive,
  list/filter)
- Report on decisions (list/filter by compliance/version/time, CSV
  export, non-compliant view)
- Inspect a failing evaluation (missing evidence vs non-compliance,
  richer output, markdown violations under --assert, UI rendering)

Update the intro to reflect the fuller scope and drop the placeholder
note that these would be added later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread tutorials/evaluate_policies_server_side.mdx Outdated
Comment thread tutorials/evaluate_policies_server_side.mdx Outdated
Comment thread tutorials/evaluate_policies_server_side.mdx
… slots

Rework the listing/searching/viewing surfaces to lead with the Kosli app
experience rather than the CLI: browse policies in the Policies sidebar,
view a policy's decisions and decision detail on its detail page, with
the CLI kept as a secondary option for scripting and CSV export.

Add <Frame> screenshot slots (policy-list, policy-decisions,
policy-decision-detail) following the repo placeholder pattern (PR #278):
1x1 transparent PNGs with descriptive alt text and {/* TODO */} markers
to replace with real UI mockups before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add a core-flow section heading so the steps aren't nested under
  Prerequisites, and turn the intro into a linked table of contents
- Cross-link the core-flow steps to the validate/manage/view sections
- Drop the non-compliance vs missing-evidence split; both surface as
  violations on the decision
- Move "Evaluate a local policy file" out of the main steps into the
  Validate and test section
- Rework "Trace and replicate" into "View and trace the decision",
  leading with viewing the decision in Kosli
- Clarify that the policy identifier is user-provided and immutable,
  not assigned by Kosli
- Recommend keeping Rego tests alongside policies in the source repo

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment on lines +360 to +362
<Frame>
{/* TODO: replace placeholder with a mockup of the Policies catalog */}
<img src="/images/tutorials/policy-list.png" alt="Policies catalog listing policies with their identifier, current version, and tags, with a search box and a tag filter" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: All three policy-*.png images are identical 1×1 pixel transparent PNGs. The {/* TODO */} comments make clear these are placeholders, which is fine for a design preview — but readers on the Mintlify preview site will see empty frames with no visual indication that they're placeholder slots. Consider either:

  • Using a visible placeholder image (e.g. a solid grey rectangle with "Mockup coming soon" text), or
  • Removing the <Frame> blocks entirely until real screenshots are ready, and noting "Screenshots will be added when the UI is built" in the text.

The current approach renders as invisible content on the live preview.

Comment on lines +202 to +206
By default the command queues the evaluation and returns without waiting; the decision is
recorded when the evaluation completes. Add `--sync` to wait for the outcome, or `--assert` to
both wait and fail the step when the policy denies — use `--assert` as a pipeline gate. Once
recorded, a decision appears against the policy — see
[View the decisions for a policy](#view-the-decisions-for-a-policy).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: This paragraph (lines 202–205) repeats the async-by-default / --sync / --assert information from the intro paragraph at lines 165–167, and the flags table at lines 199–200 already documents both flags with descriptions. Consider removing this paragraph — the table plus the first mention are sufficient.

Suggested change
By default the command queues the evaluation and returns without waiting; the decision is
recorded when the evaluation completes. Add `--sync` to wait for the outcome, or `--assert` to
both wait and fail the step when the policy denies — use `--assert` as a pipeline gate. Once
recorded, a decision appears against the policy — see
[View the decisions for a policy](#view-the-decisions-for-a-policy).
recorded, a decision appears against the policy — see

Comment on lines +155 to +157
In the beta, authoring is **CLI/API only** — the Kosli UI is read/tag-only. The exact publish
command surface (and how it relates to existing environment-policy commands) is part of what
we want feedback on.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: The callout mentions the naming question vaguely ("how it relates to existing environment-policy commands") but doesn't name the collision. kosli create policy already exists in the CLI (it creates environment policies — see client_reference/kosli_create_policy). Since this is a design preview seeking feedback, calling out the collision explicitly would help reviewers spot it:

Suggested change
In the beta, authoring is **CLI/API only** — the Kosli UI is read/tag-only. The exact publish
command surface (and how it relates to existing environment-policy commands) is part of what
we want feedback on.
In the beta, authoring is **CLI/API only** — the Kosli UI is read/tag-only. The publish
command shown here reuses `kosli create policy`, which **today** creates environment
policies — whether this should be a new command or the existing one should be overloaded
is an open design question we want feedback on.

Comment on lines +22 to +33

1. **[Author, ship, and evaluate a policy](#author-ship-and-evaluate-a-policy)** — the core loop:
write Rego locally, publish a versioned policy into Kosli, evaluate it server-side to record a
decision against a control, then view and trace that decision in Kosli.
2. **[Validate and test a policy](#validate-and-test-a-policy)** — check a policy holds up before
you ship it or gate a pipeline on it.
3. **[Manage policies](#manage-policies)** — update (creating a new version), tag, and archive
policies over time.
4. **[View the decisions for a policy](#view-the-decisions-for-a-policy)** — review and export the
decisions a policy has produced.
5. **[Inspect a failing evaluation](#inspect-a-failing-evaluation)** — understand why an
evaluation was denied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The intro lists five numbered steps as the tutorial flow, but only steps within "Author, ship, and evaluate a policy" use the <Steps> component. The remaining four (Validate, Manage, View decisions, Inspect failures) are standalone ## sections. This creates a mismatch between the promised structure and the page layout.

Consider either:

  • Reframing the intro: "This tutorial walks through the core loop (step 1), then covers reference topics for…"
  • Or restructuring so the <Steps> sections and standalone sections are introduced distinctly.

| `--policy` | The policy to evaluate — a Kosli policy **identifier** (uses its current version) or a **local** `.rego` file (see [Evaluate a local policy file](#evaluate-a-local-policy-file)). |
| `--control` | The control identifier the resulting decision is recorded against. |
| `--evaluation-context` | The trail (or trails) whose facts are evaluated. |
| `--trail` | Where the decision is recorded. Defaults to the evaluation trail; required when evaluating multiple trails. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: --trail is documented here but never appears in any of the example commands on this page. A brief example or note showing when it's needed (e.g. "when evaluating multiple trails, add --trail release-456 to specify where the decision is recorded") would help readers understand the flag's purpose.

- Add --dry-run to the evaluate policy flag table
- Reconcile the policy's fields (rego, description, input-context
  schema, identifier given at publish) with the create policy command
- Name the auditor/control-owner and application-team audiences in the
  view and inspect sections
- Strengthen the enforcement pointer (for_control + kosli assert as the
  PEP) and add a Next steps section

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| `--flow` | The flow that owns the trail(s) being evaluated and where the decision is recorded. |
| `--policy` | The policy to evaluate — a Kosli policy **identifier** (uses its current version) or a **local** `.rego` file (see [Evaluate a local policy file](#evaluate-a-local-policy-file)). |
| `--control` | The control identifier the resulting decision is recorded against. |
| `--evaluation-context` | The trail (or trails) whose facts are evaluated. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: --evaluation-context carries different semantics in kosli create policy (line 146: a schema type — trail or trails) vs kosli evaluate policy (here: the actual trail name). A reader who just saw --evaluation-context trail in the create command may trip over the overload. Consider a short clarification in the flag table, e.g. "The trail name (or names) whose facts are evaluated — not the schema declaration from kosli create policy."

(This may also be useful design feedback for the CLI surface itself.)

Comment on lines +204 to +208
By default the command queues the evaluation and returns without waiting; the decision is
recorded when the evaluation completes. Add `--sync` to wait for the outcome, or `--assert` to
both wait and fail the step when the policy denies — use `--assert` as a pipeline gate. Once
recorded, a decision appears against the policy — see
[View the decisions for a policy](#view-the-decisions-for-a-policy).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: Lines 204–206 restate the async-by-default / --sync / --assert information from lines 166–168. The flags table at lines 200–201 already documents both flags with descriptions. Consider removing this paragraph — the table plus the first mention are sufficient.

(Flagged in prior reviews as well but still present.)

Comment on lines +364 to +367
<Frame>
{/* TODO: replace placeholder with a mockup of the Policies catalog */}
<img src="/images/tutorials/policy-list.png" alt="Policies catalog listing policies with their identifier, current version, and tags, with a search box and a tag filter" />
</Frame>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: All three policy-*.png images are 70-byte, 1×1 transparent PNGs. On the Mintlify preview they render as invisible <Frame> blocks. Consider either using a visible placeholder (grey rectangle with "Mockup coming soon") or removing the <Frame> blocks entirely until real screenshots are ready, noting "Screenshots will be added when the UI is built" in the text.

kosli create policy already carries a --type enum (currently [env]), so
evaluation policies extend it rather than needing a new noun. Align the
tutorial with the real command surface:

- create policy uses the positional POLICY-NAME POLICY-FILE-PATH form
  with --type rego (env stays the default for environment policies)
- versioning re-runs create policy with --comment, replacing the
  invented kosli update policy
- list policies filters with --type rego
- explain inline that create policy is shared with environment policies,
  selected by --type

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment on lines +204 to +208
By default the command queues the evaluation and returns without waiting; the decision is
recorded when the evaluation completes. Add `--sync` to wait for the outcome, or `--assert` to
both wait and fail the step when the policy denies — use `--assert` as a pipeline gate. Once
recorded, a decision appears against the policy — see
[View the decisions for a policy](#view-the-decisions-for-a-policy).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: This paragraph (lines 204–206) restates the async-by-default / --sync / --assert behavior already covered in the intro (lines 166–168) and the flags table (lines 200–201). Consider removing lines 204–207 — the table and the first mention are sufficient.

Suggested change
By default the command queues the evaluation and returns without waiting; the decision is
recorded when the evaluation completes. Add `--sync` to wait for the outcome, or `--assert` to
both wait and fail the step when the policy denies — use `--assert` as a pipeline gate. Once
recorded, a decision appears against the policy — see
[View the decisions for a policy](#view-the-decisions-for-a-policy).
recorded, a decision appears against the policy — see

@@ -0,0 +1,491 @@
---
title: "Evaluate policies server-side"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The meta description is ~170 characters — search engines truncate beyond 160. Consider trimming:

Suggested change
title: "Evaluate policies server-side"
description: "Author and ship a versioned Rego policy into Kosli, then evaluate it server-side to record a trustworthy, replicable decision."

Comment on lines +23 to +33
1. **[Author, ship, and evaluate a policy](#author-ship-and-evaluate-a-policy)** — the core loop:
write Rego locally, publish a versioned policy into Kosli, evaluate it server-side to record a
decision against a control, then view and trace that decision in Kosli.
2. **[Validate and test a policy](#validate-and-test-a-policy)** — check a policy holds up before
you ship it or gate a pipeline on it.
3. **[Manage policies](#manage-policies)** — update (creating a new version), tag, and archive
policies over time.
4. **[View the decisions for a policy](#view-the-decisions-for-a-policy)** — review and export the
decisions a policy has produced.
5. **[Inspect a failing evaluation](#inspect-a-failing-evaluation)** — understand why an
evaluation was denied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: The intro lists five numbered parts, but only part 1 ("Author, ship, and evaluate") uses the <Steps> component — parts 2–5 are standalone ## sections. This creates a structural mismatch between the promised flow and the page layout.

Consider reframing the intro to distinguish the core tutorial (part 1) from the supporting reference sections (parts 2–5), e.g.: "This tutorial walks through the core loop (part 1). The sections that follow cover supporting operations."

Comment on lines +156 to +158
`kosli create policy` is the same command used for [environment policies](/getting_started/policies):
`--type rego` selects an evaluation policy, while the default `--type env` creates an environment
policy. In the beta, authoring is **CLI/API only** — the Kosli UI is read/tag-only.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: kosli create policy already exists in the CLI — it creates environment policies (see client_reference/kosli_create_policy). The callout hints at the relationship but doesn't name the collision explicitly. Since this is a design preview seeking feedback, making the clash unmissable would be more helpful:

Suggested change
`kosli create policy` is the same command used for [environment policies](/getting_started/policies):
`--type rego` selects an evaluation policy, while the default `--type env` creates an environment
policy. In the beta, authoring is **CLI/API only** — the Kosli UI is read/tag-only.
`kosli create policy` is the same command used for [environment policies](/getting_started/policies):
`--type rego` selects an evaluation policy, while the default `--type env` creates an environment
policy. Whether reusing the same command is the right design — or whether evaluation policies
should have their own command — is an **open design question** we want feedback on.
In the beta, authoring is **CLI/API only** — the Kosli UI is read/tag-only.

| Flag | Description |
|------|-------------|
| `--flow` | The flow the decision is recorded on (the **destination** flow). |
| `--trail` | The trail the decision is recorded on. Defaults to the evaluation trail when a single one is evaluated; required otherwise. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: --trail is documented in the flags table here but never appears in any example command on the page. A brief example or note showing when it's needed — e.g. "when evaluating a trail in another flow and recording the decision on a different trail, add --trail release-456" — would make the flag's purpose concrete.

Comment thread tutorials/evaluate_policies_server_side.mdx
pbeckham and others added 5 commits July 28, 2026 09:32
Document the contract a server-side policy is written against, so the design
preview covers the OPA-facing surface as well as the command surface: entry
point, what a policy sees in input, parameters, output, available built-ins,
evaluation time, engine version, and evaluation-failure behavior.

Publishes the input-context schemas as JSON Schema so authors can type-check
with opa check --schema, and expresses the sandbox as an OPA capabilities file
so the constraints are checkable locally rather than being a server-side
surprise. Replaces time.now_ns with an injected data.kosli.evaluation_time so
time windows stay replicable and anchored to the moment a decision is valid as
of.

Several specifics are proposals for the team to confirm before this goes to
customers: the fixed package name, the three docs.kosli.com URLs (not yet
published), data.kosli.evaluation_time, and the built-in allow/deny split.
Two TODOs remain for the pinned OPA version and the assert exit codes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Close the docs-only gaps found reviewing the tutorial against the user story
map and the V1 design decisions:

- Record both decision timestamps (requested and recorded), state that a
  decision is valid as of the requested time, and explain what happens to a
  fact that arrives between the two.
- Add --name to kosli create policy, and note that the positional argument is
  the immutable identifier for --type rego but the name for --type env.
- Say that a server-side decision satisfies a type: decision flow-template
  slot, linking the two features together.
- Resolve the trail vs trails contradiction and add a multi-trail example,
  including what happens when the context schema and reference count disagree.
- Expand what is out of scope with version pinning, replaying a decision
  against a new policy, advisory outcomes, verbose tracing, and branch or
  environment filtering, so reviewers can tell deferred from unconsidered.
- Separate authoring from execution: the repository is authoritative for
  authoring, Kosli for what ran.
- Note that sampling and CSV export are proposed for a control's decisions
  too, and are not part of controls today.

In the controls tutorial, point the API token prerequisite at the current
authentication page rather than the redirect, and align the archive wording
with the archive-as-event model.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Publishing, updating, and archiving an evaluation policy require org admin,
matching controls; evaluating and recording a decision needs only an ordinary
API token, so pipelines can invoke a policy without being able to change one.
That separation is what makes a server-side decision worth more than a
self-reported one.

Flag the consequence for the local-policy-file path, which bypasses the admin
gate because the caller supplies the Rego: call those decisions out as fit for
iteration rather than audit evidence, and ask for feedback on whether the beta
should mark or restrict them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kosli derives the parameters a policy takes from the data.params paths its Rego
depends on, rather than asking authors to declare them, and validates supplied
parameters before the evaluation is queued — in the CLI so it can be caught
locally, and on the server as the real gate.

Explain why this matters: a misspelled parameter leaves the alias undefined, so
allow falls back to false and the violations rules referencing the same alias
do not fire either, producing a non-compliant decision with no violations and a
failed pipeline with nothing to fix. Note the limitation that dynamically
computed parameter names cannot be derived statically.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…drail

Act on three open review questions.

Review (question 2): add "Review a policy change" under Manage policies. A
policy is the code that decides whether a control passes, so changing it is a
change to the control environment. In the beta the review happens in the policy
repository — protected branch, CODEOWNERS per policy so the accountable control
owner sees changes to their control, publish from CI on merge so the reviewed
commit is the published version, and --comment pointing back at the change
record. State plainly that Kosli has no in-product approval step and ask whether
it needs one.

Replicability (question 3): stop claiming a re-run yields the same decision and
instead show how to do it. Tabulate what a decision records, including the
policy content digest and the engine version, and add "Re-run a decision
offline" — export the trail moment, params, injected evaluation time, and policy
source, then evaluate with stock OPA. This is the stronger audit story because
it does not ask anyone to trust Kosli to check Kosli, and the export doubles as
an opa test regression fixture.

Cross-flow evidence (question 5): keep the capability, record the source flow
and trail on the decision so a reviewer can see a release decision was reached
from another flow's evidence, and warn that evidence from a low-assurance flow
can otherwise close a control that gates production.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pbeckham and others added 2 commits July 28, 2026 10:40
The page had grown to the longest tutorial in the repo, and a reader had to get
through a comparison table, prerequisites and four steps before seeing what the
feature actually is.

Add a "Start here" opener: the whole loop as two commands, a short in-scope and
out-of-scope summary so reviewers do not spend feedback on things already
deferred, and cards routing the three audiences — governance platform engineer,
auditor, application team — to where they should begin.

Take the fifteen-row flag table out of the tutorial path. Inline only the four
flags that carry the meaning, warn about the destination versus source
distinction that the flag names invite people to misread, and collapse the full
table into an accordion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the three 70-byte placeholder images with rendered mockups, and add a
fourth for the Policy tab. Chrome and palette are matched against the real
Controls screenshots in images/tutorials/controls-*.png, so the proposed pages
sit alongside the shipped ones without looking foreign. Every page carries a
"Mockup - proposed design" marker, because they depict a UI that does not exist.

Add a "View a policy" section for the Policy tab, which is the default view on a
policy: the read-only policy-as-code Kosli actually runs, for the version you
choose. A name and description can only claim what a policy checks; the source
is what lets a control owner answer whether it meets the control objective, and
what lets an auditor check the digest a decision cited. Files in a multi-file
bundle are listed with the entrypoint, context schema and parameters read;
shipped tests are listed and marked not executed.

Note in the ship step that a policy can be a directory of Rego files stored as
one versioned bundle with one digest, so a decision still cites one thing.

Mockup sources live in mockups/policies-ui/ with regeneration instructions;
generated HTML and PNGs are gitignored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mintlify auto-applies every .css file it finds in the project — style.css is not
referenced from docs.json, it is picked up by convention — so committing
mockups/policies-ui/kosli-mock.css injected the mockup chrome into every docs
page. It carries a universal margin/padding reset and a fixed-width flex body,
which broke the site layout.

Move the stylesheet into build.py, which writes it at generation time, and
gitignore it alongside the generated HTML and PNGs. No .css is committed outside
style.css. Renders are byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two corrections to the offline re-run and the command surface.

The export is now kosli get evaluation-context, not kosli get decision. The
evaluation context is the entity that actually holds what an evaluation was
given, so exporting the decision meant reaching through it to find the inputs.
More importantly a context exists from the moment an evaluation is queued, so
this also works when there is no decision to look at — a queued evaluation, or
one that failed, which is the case you most want to inspect. Decisions now name
the context they came from, in the decision detail mockup and in the
recorded-on-the-decision table, so the context ID is one hop away.

Rename --evaluation-context to --context. The old name described the entity
Kosli builds rather than what you pass, and it would have blocked the obvious
spelling for re-running a stored context (--context evc_...). --context also
survives the scope growing beyond attestations: a reported workload is a state
report, not a fact, so "facts" would have been wrong for it, while
--context snapshot=... reads naturally. --context-schema needs no change.

Note in the flag description that this selects what to evaluate, not a cluster
or account — kubectl has trained this audience to read --context the other way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A reviewer read "the first pair says where the decision is written, the second
says which facts are read" as describing two pairs of flags, and could only find
one. --context is a single flag, so the phrasing was wrong.

Name each flag instead of counting them, and lead with the read before the write
so the sentence follows the order the command works in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Republishing a policy whose bundle, name, description and context schema are
unchanged creates no new version, reports that, and exits 0. Without this the
"publish from CI on merge" pattern the page recommends mints byte-identical
versions on every merge and the version history stops being useful — and a
declarative apply could never be idempotent.

Tabulate what does and does not count as a change: source edits version (the
digest is over the bytes, so reformatting counts), name/description/schema edits
version because a version's description is part of the audit record, and a
--comment on its own does not, since a comment describes a change that isn't
there.

Comparison is against the current version rather than the whole history, so
republishing an earlier bundle is a real change and does create a version. That
is how a rollback works, and it stays visible in the history.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
<Step title="Ship a versioned policy into Kosli">

Publish the policy into Kosli via the CLI (or API). The positional argument is the policy's
**stable identifier** (`pr-approved` here) — this is what decisions and evaluations use to refer to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure i understand the ('pr approved' here) part?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah i understand it's for the example further below.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a few of these in the doc that confused me that we could change to "in the example below" but it's non-material to this for now

--name "Pull request approved"

# Evaluate it from your pipeline, as often as you like.
kosli evaluate policy \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written I think this example gets rejected by the parameter validation described later on. The Parameters section says a missing parameter is rejected before the evaluation is queued?

Comment on lines +161 to +171
allow if {
every pr in input.trail.compliance_status.attestations_statuses[pr_attestation_name].pull_requests {
count(pr.approvers) > 0
}
}
violations contains msg if {
some pr in input.trail.compliance_status.attestations_statuses[pr_attestation_name].pull_requests
count(pr.approvers) == 0
msg := sprintf("pull-request %v has no approvers", [pr.url])
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Further down, "Inspect a failing evaluation" says a missing attestation surfaces as a violation, but with this policy it doesn't: a missing slot leaves this rule undefined too, so you get a denial with an empty violations list and nothing to fix. A second rule for the missing slot keeps that claim true:

Suggested change
allow if {
every pr in input.trail.compliance_status.attestations_statuses[pr_attestation_name].pull_requests {
count(pr.approvers) > 0
}
}
violations contains msg if {
some pr in input.trail.compliance_status.attestations_statuses[pr_attestation_name].pull_requests
count(pr.approvers) == 0
msg := sprintf("pull-request %v has no approvers", [pr.url])
}
allow if {
every pr in input.trail.compliance_status.attestations_statuses[pr_attestation_name].pull_requests {
count(pr.approvers) > 0
}
}
violations contains msg if {
some pr in input.trail.compliance_status.attestations_statuses[pr_attestation_name].pull_requests
count(pr.approvers) == 0
msg := sprintf("pull-request %v has no approvers", [pr.url])
}
violations contains msg if {
not input.trail.compliance_status.attestations_statuses[pr_attestation_name]
msg := sprintf("expected attestation %v is missing from the trail", [pr_attestation_name])
}

<Step title="Ship a versioned policy into Kosli">

Publish the policy into Kosli via the CLI (or API). The positional argument is the policy's
**stable identifier** (`pr-approved` here) — this is what decisions and evaluations use to refer to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a few of these in the doc that confused me that we could change to "in the example below" but it's non-material to this for now

pr_attestation_name := data.params.pr_attestation_name
default allow = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seemingly opa fmt would re-write this. Consistency nit only

Suggested change
default allow = false
default allow := false

against controls, validating and testing policies, and reviewing or exporting the decisions a policy
has produced.

**Not in:** evaluating anything other than trails, evaluating the contents of attachments, composing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So how would we evaluate a SARIF file - is that still to be done client side and then our loaded policies only check for if it ran and how many issues it had?

--assert
```

Kosli runs the policy against the trail's attestations and records a **decision** against control

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is directly below the first example block of text and talks about trails but it's not obvious as to why trails suddenly were mentioned as flows in there and artifact fingerprint.

Should we consider saying how the trails are chosen / found?


**Not in:** evaluating anything other than trails, evaluating the contents of attachments, composing
several policies into one, scheduled evaluations, pinning an evaluation to a policy version, and
advisory-only outcomes. [Full list, with reasons](#whats-out-of-scope-for-the-beta) — worth reading

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This link was not working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants