docs: add proposed server-side policy evaluation tutorial - #338
docs: add proposed server-side policy evaluation tutorial#338pbeckham wants to merge 22 commits into
Conversation
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 finished @pbeckham's task in 3m 3s —— View job Review: Server-side policy evaluation tutorial
SummaryStrong design-preview tutorial that clearly separates what's proposed from what exists today. The FindingsBy file:
By file: 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: No issues. CLAUDE.md compliance
Diátaxis assessmentThe page is titled as a tutorial but is a hybrid: part 1 (the Overall6 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 |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
- 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 |
There was a problem hiding this comment.
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.
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>
… 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>
| <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" /> |
There was a problem hiding this comment.
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.
| 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). |
There was a problem hiding this comment.
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.
| 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 |
| 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. |
There was a problem hiding this comment.
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:
| 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. |
|
|
||
| 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. |
There was a problem hiding this comment.
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. | |
There was a problem hiding this comment.
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. | |
There was a problem hiding this comment.
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.)
| 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). |
There was a problem hiding this comment.
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.)
| <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> |
There was a problem hiding this comment.
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>
| 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). |
There was a problem hiding this comment.
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.
| 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" | |||
There was a problem hiding this comment.
Suggestion: The meta description is ~170 characters — search engines truncate beyond 160. Consider trimming:
| 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." |
| 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. |
There was a problem hiding this comment.
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."
| `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. |
There was a problem hiding this comment.
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:
| `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. | |
There was a problem hiding this comment.
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.
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>
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 |
There was a problem hiding this comment.
Not sure i understand the ('pr approved' here) part?
There was a problem hiding this comment.
Ah i understand it's for the example further below.
There was a problem hiding this comment.
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 \ |
There was a problem hiding this comment.
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?
| 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]) | ||
| } |
There was a problem hiding this comment.
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:
| 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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
seemingly opa fmt would re-write this. Consistency nit only
| 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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
This link was not working
What
Documentation-driven development for the server-side Kosli Evaluate project (kosli-dev/server#6267). Adds a tutorial describing the proposed V1
kosli evaluate policyflow, 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:
allowmandatory).kosli evaluate policyruns server-side and records a decision against a control; synchronous.attestation_ids, policy version, params, timestamp; includes the T / T+1 / T+2 temporal-correctness case.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
kosli attest decisionandkosli evaluate trailflags.--compliantand evidence (--attachments/--user-data) are intentionally not flags — the server derives compliance and captures the policy/report/violations as output.kosli create policy, which collides with the existing environment-policy command — flagged inline as an open question.inputshape 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