Repository navigation
core: take the policy and params as arguments to ergo.report - #163
Merged
Merged
Conversation
First step of #113. The `subjects` section comes next, as another section of the same policy object. `ergo.report(input, policy, params)` replaces `ergo.report(input, requirements)` and `ergo.report_with_params`. The policy is an object whose `requirements` section holds the requirements, and ergo no longer reads `data.params`. The report depends only on the three arguments, so the policy that calls ergo decides where they come from, and can live in any package, `params` included. Every call changes: ```rego report := ergo.report(input, {"requirements": requirements}, {}) ``` A policy loaded from YAML gives each argument a default, so a file with no `requirements:` or a run with no params gives a report that isn't compliant instead of no report at all: ```rego default requirements := {} requirements := data.requirements default params := {} params := data.params report := ergo.report(input, {"requirements": requirements}, params) ``` ergo ignores sections of the policy it doesn't know. Changes the report: requirements given as a non-empty string, like `ergo.report(input, "x")`, used to be compliant with no rows. A policy or `requirements` that isn't an object is now never compliant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 8, 2026
jbpros
marked this pull request as ready for review
October 8, 2026 12:43
jbpros
added a commit
that referenced
this pull request
Oct 8, 2026
…nts (#164) Second step of #113, after #163. This now includes what was #165. A policy has a `subjects` section next to `requirements`. A requirement names its subject, and `subject_type` is gone: ```yaml subjects: SBOM package: from: [components] id: [name] locked SBOM package: of: SBOM package applies_to: locked: { op: present, path: [lock_release] } requirements: versions: description: Every SBOM package the lockfile pins has the version it pins subject: locked SBOM package checks: matches_lock: { op: compare, left: [sbom_release], right: [lock_release], cmp: eq } ``` The README policy written this way gives the same report, byte for byte, as `main` gives for the README's current policy. ## Subjects - A subject has `from`, `id`, `applies_to`, an optional `description` (checked to be a string, not copied into the report yet) and an optional `of`. - The subject's name is the `type` in every row and the word in the descriptions of ergo's own checks (`The in-scope deployment count is at least 1`), as `subject_type` was. - A requirement must name a subject. `from`, `id`, `applies_to` and `subject_type` on a requirement are unknown fields. - `applies_to` lives only on subjects. - Several requirements can name the same subject, and each checks it with its own rows. - A subject no requirement names isn't read, even if it's written wrong. ## Subjects built on subjects A subject with `of` takes `from` and `id` from the end of its chain, which can be any length, and keeps the items that pass every filter along it. This came from SDLC-CTRL-0004 in sdlc-policies, which has two requirements on `SBOM package` with different filters. They become `locked SBOM package` and `non-exempt SBOM package`, both built on `SBOM package`. - A subject with `of` can't have its own `from` or `id`: `not allowed with of`. - A chain that comes back to a subject already in it: `leads to a loop`. - A filter name used twice along a chain: `also a filter of a subject it builds on`. - Rego functions can't recurse, so ergo follows the chain with `graph.reachable`, which runs inside the Wasm module and in the JS runtime. ## Written wrong Each of these fails `$well_formed` on every requirement it affects, and that requirement finds no subjects: - a missing `subject`, or one not in `subjects` - a `subjects` or a subject that isn't an object - any of the chain problems above Problems are named after the subject they're in, like `{"name": "subjects.deployment.from", "value": ["not a list"]}`, and listed after the requirement's own. The `$well_formed` expression now starts with `subject in subjects and`. ## Wasm memory Wasm doesn't cache function calls and frees nothing until an evaluation ends. Following each subject's chain again on every row ran CI's Wasm job out of memory, so ergo now works out each subject once per report and looks it up after that. The suite's Wasm memory is now between `main`'s and what this PR used before that fix. ## Tests The existing tests still write requirements the old way. The `report(doc, reqs)` helper from #163 moves each one's `from`, `id` and `applies_to` into a subject named after its `subject_type`, so they keep checking the same things. Tests about subjects themselves call `ergo.report` with a full policy. Removing each new guard (the subject problems in `$well_formed`, an unresolved subject reading no `from`, the loop check, the chain-wide filter check) makes a test fail. ## Checks - Everything in `AGENTS.md`, locally: 939/939, `--strict-builtin-errors`, Regal, no comments, no uncovered lines, and the Wasm JS runtime. CI ran OPA 1.20.2 and Wasm on this code as #165, and they passed. - Every changed example in `README.md` and `REFERENCE.md` was run. Two REFERENCE violations were missing the `failed_items` that #161 added, and REFERENCE said a subject's bad `applies_to` fails `$applies` as `absent` when it's `ill_formed`, so I fixed both. - A 3,000-subject report takes the same time as on #163 (1.47s). - Downstream, with each requirement's subject fields moved into a subject: server's npm-bump passes 6/6, and sdlc-policies fails the same tests as with `main`'s ergo. `site/` still shows `subject_type` and the old call. That'll be a `site:` PR after this lands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
jbpros
added a commit
that referenced
this pull request
Oct 8, 2026
The landing page still showed `subject_type`, `from` and `id` on each requirement, and `ergo.report(input, data.requirements)`. Both have been gone since #163 and #164. - The two hero policies (deploy approval and four eyes) and the "Write the policy" example now define a subject and name it from the requirement. The file name in them is `policy/policy.yaml`, as in the README. - The notes beside the example say what subjects, `applies_to` and `subject` do. - The "Write a policy" step shows the three-argument `ergo.report` call, split over four lines so it fits its box. - The bakery page says what its `batch` subject does. Its YAML and Rego come from `examples/baking`, which #164 already updated. The reports on the page are unchanged: I ran both hero policies and the bakery example on `main`, and the passes and causes match what's shown. `hugo -s site` builds, no built page mentions `subject_type` except the reference's sentence calling it an unknown field, and I checked the landing page at 1280px and 390px wide with no sideways scrolling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First step of #113. The
subjectssection comes next, in a PR stacked on this one, as another section of the same policy object.ergo.report(input, policy, params)replacesergo.report(input, requirements)andergo.report_with_params. The policy is an object with arequirementssection, and ergo no longer readsdata.params, so the report depends only on its three arguments.Every call changes. In Rego:
From YAML, with a default for each argument so a missing section or missing params still gives a report:
What else changes
requirementsthat isn't an object is never compliant. Before this,ergo.report(input, "x")reported{"compliant": true, "requirements": {}, "results": []}.requirementsalready fails because the policy then has nothing to check. Say if you'd rather they failed.report(doc, reqs), that wraps the requirements and readsdata.params, so the manywith data.params astests keep working. In the subjects PR, only that helper has to change.$$paramsexample inREFERENCE.md, which was missing thefailed_itemsthat core: never report missing as a cause in failed_items #161 added.site/content/_index.mdstill shows the old call. It'll get its ownsite:PR.Checks
AGENTS.md:opa check --strict,opa fmt,opa test(912/912),--strict-builtin-errors, Regal, no comments, no uncovered lines.--timeout 30s, as CI uses).main. With no YAML file, the YAML version reports{"compliant": false, "requirements": {}, "results": []}.ergo.reportcall moved to the new signature: kosli-dev/server's npm-bump tests pass (6/6). kosli-dev/sdlc-policies fails the same 7 tests with this branch as withmain's ergo. Those failures come from earlier changes, and all pass with the copy it has pinned.🤖 Generated with Claude Code