Skip to content

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

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

docs: add proposed server-side policy evaluation tutorial#338
pbeckham wants to merge 40 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

@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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


Review: tutorials/evaluate_policies_server_side.mdx and related changes

  • Read CLAUDE.md and doc-review skill
  • Identify changed docs files
  • Review tutorial page content
  • Review navigation config
  • Check for broken links and relative paths
  • Post findings

Navigation and structure

New page registered in config/navigation.json — confirmed. tutorials/evaluate_policies_server_side is added to the "Evaluation" group alongside evaluate_trails_with_opa. No issues.

All internal links resolve — verified all 14 root-relative links (/getting_started/install, /tutorials/working_with_controls#creating-a-control, etc.) against actual files. All exist. No relative links (../) found anywhere.

working_with_controls.mdx changes — two small fixes: a broken prerequisite link (/getting_started/service-accounts/getting_started/authenticating_to_kosli) and improved archive/unarchive wording. Both are clean improvements.


Findings

Suggestion — Schema/capabilities URLs reference files that don't exist yet

evaluate_policies_server_side.mdx:518-519, 612

Three URLs are presented as published JSON Schema assets readers can download:

  • https://docs.kosli.com/schemas/evaluation-input/trail/v1.json
  • https://docs.kosli.com/schemas/evaluation-input/trails/v1.json
  • https://docs.kosli.com/rego/capabilities/v1.json

None exist in the schemas/ directory or anywhere in this repo. The page shows curl commands that download them (lines 542, 612), which will 404 today. Since the page already carries a prominent "not yet available" banner, this is fine for the design preview, but worth noting for when this moves out of draft — the schema files need to be created or hosted before the feature ships.

Suggestion — Diataxis: page is a hybrid tutorial/reference

The page is filed under "Tutorials" and follows a teach-by-doing structure in the first half (Steps component, progressive examples). The second half (The Rego authoring contract, Manage policies, View the decisions) shifts into reference material. This is acknowledged in the intro's <CardGroup> which routes different audiences to different sections, and the PR description calls it out. For a design preview gathering feedback, the hybrid form works well — it gives every reviewer one page to read. When the feature ships, splitting the authoring contract and management sections into separate reference pages would align better with Diataxis.

Suggestion — --context syntax has no error examples

The --context trail=<trail_id> and --context trail=<flow_id>/<trail_id> syntax is documented, but there's no mention of what happens if the format is wrong (e.g., missing the trail= prefix, or naming a nonexistent trail). Since --context is now required and carries unusual typed-reference syntax, a brief note on the error the CLI returns would help readers debug a mistyped flag.


Overall assessment

This is a thorough, well-structured design preview for a significant feature. The 40-commit history shows extensive iteration incorporating review feedback from multiple reviewers — the page is in strong shape:

  • Frontmatter — present and correct, description is within 160 chars.
  • Writing style — active voice, American spelling, correct product terminology ("Kosli", "attest", "audit trail"). The AI-generation tells from tooky's review (rhetorical flourishes, chatty tone) have been cleaned up.
  • Components — good use of <Steps>, <Accordion>, <Info>/<Warning>/<Tip>/<Note>, <CardGroup>, <Frame>. Callouts are used purposefully, not excessively.
  • Images — all four policy-*.png files are real UI mockups (120-220KB), replacing the earlier 1×1 placeholders.
  • Links — all root-relative, all resolve. No relative links. Anchor links to headings within working_with_controls.mdx are correct.
  • No TODOs remaining — both {/* TODO */} comments flagged in prior reviews have been removed.
  • No --dry-run — dropped per the latest commit.

The open review threads from tooky and AlexKantor87 raise substantive design questions (timestamps, SARIF handling, policy bundles, roles) that are mostly about the feature design rather than the docs quality — the right kind of feedback for a design preview.

@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
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 Outdated
Comment thread tutorials/evaluate_policies_server_side.mdx Outdated
Comment thread tutorials/evaluate_policies_server_side.mdx
Comment thread tutorials/evaluate_policies_server_side.mdx
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 Outdated
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 Outdated
Comment thread tutorials/evaluate_policies_server_side.mdx
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 Outdated
Comment thread tutorials/evaluate_policies_server_side.mdx
pbeckham and others added 3 commits July 29, 2026 10:39
The flag names the schema of the data passed to the policy as `input`,
so `--input-schema` says what it does. It also stops the publish and
evaluate commands sharing a word for two unrelated things: policies are
now published with `--input-schema` and evaluated with `--context`,
which reviewers had read as one overloaded flag.

Renames the flag on `kosli create policy` and `kosli validate policy`,
and the concept in prose from "input-context schema" to "input schema".
No behavior change: the accepted values are still `trail` and `trails`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Drop the restatement of the async default. It was stated three times
  within 65 lines: the intro to the command, a paragraph below the flag
  table, and the --sync row of the flag table itself. The paragraph now
  only carries what was unique to it — --assert as the pipeline gate,
  and the pointer to the decisions section.
- Replace "here" with "in the example below" where it pointed forward at
  an example rather than at nearby text, which reviewers read as
  referring to the paragraph they were in.
- Trim the meta description from 163 to 130 characters, under the 160
  search engines will show.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A policy cannot open the files attached to an attestation, which left
readers asking how a SARIF file or SBOM gets evaluated at all.

Documents the pattern: summarize the attachment into JSON at attestation
time and pass it as --user-data, which is in the policy input, while
keeping the file itself on the same attestation with --attachments. The
summary is what the policy reads; the attachment is what makes the
summary checkable, since an auditor can confirm it against the file it
came from.

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

tooky commented Jul 29, 2026

Copy link
Copy Markdown

Thoughts

  • “Running evaluation inside Kosli changes what a decision means”
    • Why does it change what it means?
  • I’m not sure why it keeps talking about “a core loop”?
  • Probably need a new role or two for control and policy management
  • I’m not sure if we should assume --flow is defaulted into context on kosli evaluate policy
    • The note “a command that names its source and its destination is easier to read back months later than one relying on a default.” Is describing a potential footgun??
  • What’s the behaviour without sync? How can the customer check for status of evaluation that is async?
  • “Be deliberate about which flows may satisfy a production control.” - wondering if this feels a bit alarmist to include at this stage. I think maybe there’s an FAQ section we could add?
  • “A decision carries two timestamps. “ Really keen that we make sure this has been seen widely by the team. I might need to see some examples to help understand what makes sense.
    • Maybe it would need stating explicitly that request time would be used as the query time for the trail data
    • Alternative we could consider is that if an evaluation request time doesn’t return the latest moment we invalidate the request somehow?
  • Did you consider any alternatives for trail and flow “co-ordinates” that the <flow>/<trail> format (e.g. trail=build-ci/build-41)?
  • “summarize it into JSON when you make the attestation and pass it as --user-data” <- this may depend on attestation type, its probably part of the main attestation_data for a custom attestation type schema?
  • Have been experimenting with some different policy output ideas for richer feedback, we may be able to provide “libarary” rego they can choose to use along with their rego, maybe we should think about how this would work now. e.g see Claude conversation:
  • Probably not v1 - but we might have to support multiple Engine and Rego versions? For backwards compatibility purposes? Perhaps the policy should be published with a version it is tested against? Compatible with?
  • --dry-run in Kosli cli usually does not hit the server? Should we use a different flag
    • Can we evaluate with --assert but no decision recorded
  • Evaluate a local policy file – wondering about recording the sha256 of the policy?
  • Thinking about the “Review a policy change” section and whether we should help people create trails for their policy management processes
  • I really like that we’ve gone straight for policy bundles - what does that require from us for validating the bundle on upload?
    • Does the “Re-run a decision offline” section need to be updated to account for policy bundles?
    • Out of scope says: Composable policies — composing many authored policies into one versioned policy? Is that different from a policy bundle

AI generation tells

  • Opening para has a few rhetorical flourishes (e.g. “one it can vouch for”)
  • “The whole loop in two commands”, “Publish a policy, then evaluate it. That is the feature:”
  • Some of the language feels a bit chatty “worth reading before you send feedback, so you don’t spend it on something already deferred.”
  • “Note that --context is doing real work here:”

pbeckham and others added 15 commits July 30, 2026 09:09
Review flagged several phrases as reading like generated marketing copy
rather than documentation. Each is replaced with the underlying fact.

- "records the decision itself — one it can vouch for" states the claim
  instead of the mechanism; say what is recorded alongside the decision.
  Same for the comparison table's "a decision Kosli computed and can
  vouch for".
- "The whole loop in two commands" / "That is the feature:" sells rather
  than informs.
- "worth reading before you send feedback, so you don't spend it on
  something already deferred" — the Feedback section already covers this.
- "Note that --context is doing real work here:" editorialises about the
  sentence that follows it.
- "core loop" was a drafting label that meant nothing to readers; three
  uses replaced with what the section actually covers.
- "changes what a decision means" asserted significance without saying
  what changed. It is who computed the decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
--context defaulted to the trail named by --flow / --trail. Review
pointed out that the note defending the default was describing a footgun
rather than removing one: with the default in place, a command that reads
and writes different trails looks almost identical to one that does not.

Makes --context required, the same decision already taken for --trail and
for the same reason — this command does not infer trails. Every example
on the page already passed it explicitly, so no example changes.

This also removes the warning that existed only to manage the hazard the
default created; three lines of plain description replace it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The --context cell was a ~700 character paragraph inside a table, which
does not read on narrow viewports. The cell now states what the flag is
and that it is required; the reference-type syntax, repeat semantics, and
room for future context types move to prose under the table.

Also drops a "by default the decision is recorded on the same trail whose
facts you evaluate" left over from when --context had a default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Result" names what an evaluation produces better than "output", and
pairs with the input schema: input schema in, result schema out.

Renames the seven uses that mean the policy's return value, including the
"Output" section heading and "output schema" in the validation list. The
four that mean something else are unchanged: the --output flag on
kosli list decisions and kosli get evaluation-context, OPA's print/trace
output, and the draft banner's "commands, flags, and output shown".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A policy can be published as a bundle of several Rego files, but two
places still assumed a single file.

"Re-run a decision offline" exported the policy as one policy.rego and
replayed it with --data, which cannot round-trip a multi-file bundle. The
export is now a policy/ directory laid out as published, replayed with
--bundle so the same command works for one file or many. Says that the
layout is preserved so the digest on the decision can be verified, not
just the outcome re-run.

The "composable policies" scope bullet read as a description of a bundle,
which is what prompted the question. It now names the distinction:
composing separately published policies is out of scope, one policy
spanning several files is not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The publish step offered a directory of Rego files, but every other
mention of --policy said "file" only — so a reader who took up that offer
had no documented way to validate or ad-hoc-evaluate what they wrote.

--policy now consistently takes a single .rego file or a directory
holding one policy's bundle, on create, validate and evaluate. States
that a directory is one policy's bundle, not a collection of separate
policies, which "a directory of them" left ambiguous.

Renames "Evaluate a local policy file" to "Evaluate a local policy" since
it is no longer file-only, and updates the one link to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The validation list was written for a single file, leaving it unclear
whether a multi-file bundle is checked as one unit. States that it is,
without specifying the mechanism, and that publishing runs the same
checks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two terms a bundle author needs that the page left implicit.

Tests: the page states twice that tests are stored but never executed,
which depends on Kosli telling a test file from a policy file. Names the
convention (*_test.rego) so a misnamed file does not silently become
evaluated policy.

Roots: previously appeared only inside the policy-tab screenshot alt
text, never defined. Glossed where the tab's contents are described.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two {/* TODO */} comments did not render, so the open questions they held
were visible only in source. The surrounding prose reads complete without
them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follows the earlier pass on flagged phrasing with a sweep for the same
patterns elsewhere. Each cut removes a verdict about a fact, not the fact.

- "the strongest form of the audit story, because it doesn't ask anyone to
  trust Kosli to check Kosli" — the sentence after it states the point.
- "nobody has to trust that version 3 is still what version 3 was" — the
  preceding clause already says a digest proves the bytes.
- "a summary with no attachment behind it is just another self-reported
  number" — a closer, after the reason was already given.
- "what makes a server-side decision worth more than a self-reported one"
  — keeps who can do what, drops the judgement.
- "This is what makes publishing safe to run" — leads with the fact:
  publishing is idempotent.
- The control owner's question was framed twice, ~80 lines apart, in the
  same construction. Keeps the one in the review workflow, where it is
  actionable.
- Drops the trade-booking analogy from the two-timestamp explanation, and
  a filler "Note that".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Evaluation is asynchronous by default, but the page never showed what the
default returns and gave no way to observe it — every example passed
--sync or --assert. Review asked what the behaviour is without --sync and
how a customer checks status.

The evaluation context answers both, since it exists from the moment an
evaluation is queued and already holds the requested and recorded times.

- Shows what the asynchronous command returns: the context that will
  carry the outcome.
- Adds "Check an asynchronous evaluation": kosli get evaluation-context
  reports STATUS (queued, completed, failed) and names the decision it
  produced. Also documents --show-input for returning the policy input
  that was evaluated, matching the flag of the same name on the
  client-side evaluate commands so jq '.input' and kosli evaluate input
  keep working.
- States that a typed error lands on the context, so a broken policy is
  distinguishable from a non-compliant artifact without --sync.

Corrects the fixture export from --output fixture to --fixture. In the
CLI, --output selects a format ([table, json, markdown]) and never
content, so overloading it here contradicted every other kosli get.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review read "Be deliberate about which flows may satisfy a production
control." as alarmist for a design preview of a feature nobody can use
yet. The sentence added a directive tone without adding information — the
rest of the callout already states the gap and what to do about it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review asked for the request timestamp to be stated explicitly as the
query time for trail data, and for a worked example to make the model
concrete.

States the mechanism: Kosli fixes the trail moment (which attestations
are in scope) at the requested/enqueue time, then queries and serializes
that data only when the evaluation executes — always as of the fixed
timestamp, not as of whatever the trail contains by then. This is what
makes a queued evaluation deterministic regardless of queue delay, and
is why no drift-detection or invalidation mechanism is needed for a fact
that lands mid-queue: the query never looks at "now," only at the
timestamp fixed at enqueue.

Replaces the abstract "facts arrive mid-flight" paragraph with a worked
example using the same evaluation context (ec_7f3a91c2) and timestamps
already introduced in "Check an asynchronous evaluation", so the same
example carries through both sections instead of introducing new
arbitrary numbers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review pointed out that recommending --user-data for a summarized
attachment may depend on attestation type — it's probably part of the
main attestation_data for a custom attestation type schema. Checked
against the CLI reference: --attestation-data exists only on
kosli attest custom, documented as the custom attestation data validated
against the type's schema. --user-data is documented as "additional data"
on every attestation command, never the schema-validated payload.

So the earlier guidance to always use --user-data was incomplete: for a
custom attestation type you define, --attestation-data is the schema-
validated field and the better fit for a recurring summary shape (finding
counts, severities, package IDs). --user-data remains correct for
built-in types (generic, snyk, sonar, and others) that have no schema
field for the summary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review asked what --dry-run's "evaluate without recording" actually adds
once every other way to test a policy is accounted for:

- Rego correctness and schema checks: kosli validate policy — no trail
  data touched, nothing recorded.
- Behavioral correctness: Rego unit tests / opa test, including
  regression fixtures already exported from past decisions.
- Behavior against real trail data: "Evaluate a local policy" already
  covers this. It records a decision, but the page already says to treat
  that as expected — the Warning right below it states these decisions
  cite an unpublished source, should be treated as iterating rather than
  audit evidence, and that the beta is soliciting feedback on marking or
  restricting them. Once that one real evaluation exists, its exported
  fixture can be re-run locally with opa eval against any number of
  different candidate policies, for free, with nothing further recorded.

--dry-run also collided with the CLI-wide convention (--dry-run means "no
data sent, exits 0 regardless of errors" everywhere else), which this
turn's discussion had been trying to fix by renaming. Removing the
overlapping capability is simpler than renaming it.

Drops the --dry-run flag-table row, the "Run an ad-hoc evaluation"
section, and the dangling --dry-run mention at the end of "Evaluate a
local policy".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread tutorials/evaluate_policies_server_side.mdx
Comment thread tutorials/evaluate_policies_server_side.mdx
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.

4 participants