Skip to content

core: take the policy and params as arguments to ergo.report - #163

Merged
jbpros merged 1 commit into
mainfrom
core/pure-report
Oct 8, 2026
Merged

jbpros merged 1 commit into
mainfrom
core/pure-report

Conversation

@jbpros

@jbpros jbpros commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

First step of #113. The subjects section comes next, in a PR stacked on this one, 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 with a requirements section, and ergo no longer reads data.params, so the report depends only on its three arguments.

Every call changes. In Rego:

report := ergo.report(input, {"requirements": requirements}, {})

From YAML, with a default for each argument so a missing section or missing params still gives a report:

default requirements := {}

requirements := data.requirements

default params := {}

params := data.params

report := ergo.report(input, {"requirements": requirements}, params)

What else changes

  • A policy or requirements that isn't an object is never compliant. Before this, ergo.report(input, "x") reported {"compliant": true, "requirements": {}, "results": []}.
  • ergo ignores sections of the policy it doesn't know. I left them out of the report rather than failing on them because there's no row to say why the report failed, and a misspelled requirements already fails because the policy then has nothing to check. Say if you'd rather they failed.
  • The tests call a helper, report(doc, reqs), that wraps the requirements and reads data.params, so the many with data.params as tests keep working. In the subjects PR, only that helper has to change.
  • I also updated the $$params example in REFERENCE.md, which was missing the failed_items that core: never report missing as a cause in failed_items #161 added.
  • site/content/_index.md still shows the old call. It'll get its own site: PR.

Checks

  • Everything in AGENTS.md: opa check --strict, opa fmt, opa test (912/912), --strict-builtin-errors, Regal, no comments, no uncovered lines.
  • OPA 1.20.2 in Docker: fmt, check and test pass (with --timeout 30s, as CI uses).
  • Wasm in Docker (912/912), and the Wasm JS runtime (911/911).
  • The README examples, both the Rego and the YAML versions, give the same report as main. With no YAML file, the YAML version reports {"compliant": false, "requirements": {}, "results": []}.
  • Downstream, with each ergo.report call 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 with main's ergo. Those failures come from earlier changes, and all pass with the copy it has pinned.

🤖 Generated with Claude Code

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>
@jbpros jbpros added core Relates to the ergo library or format themselves. impacts-ergo-policies Impacts ergo policy schema. Careful design attention needed. impacts-ergo-reports Impacts ergo report schema. Careful design attention needed. labels Oct 8, 2026
@jbpros
jbpros marked this pull request as ready for review October 8, 2026 12:43
@jbpros
jbpros merged commit 8d8b7ce into main Oct 8, 2026
5 checks passed
@jbpros
jbpros deleted the core/pure-report branch 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Relates to the ergo library or format themselves. impacts-ergo-policies Impacts ergo policy schema. Careful design attention needed. impacts-ergo-reports Impacts ergo report schema. Careful design attention needed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant